package com.joysuch.wwyt.bp.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.zlg.common.enums.BusinessTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.github.yitter.idgen.YitIdHelper;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.joysuch.wwyt.alert.service.NoticeWaitingTriggerListService;
import com.joysuch.wwyt.app.bean.AppInvestigateDto;
import com.joysuch.wwyt.attendance.service.AttendanceApiUserService;
import com.joysuch.wwyt.audit.entity.AuditLogRecord;
import com.joysuch.wwyt.audit.service.AuditLogProcessor;
import com.joysuch.wwyt.bp.bean.BpBaseJobBean;
import com.joysuch.wwyt.bp.bean.BpBaseUserBean;
import com.joysuch.wwyt.bp.bean.BpBaseUserJobBean;
import com.joysuch.wwyt.bp.bean.BpInvestigatePlanReqBean;
import com.joysuch.wwyt.bp.bean.investigate.*;
import com.joysuch.wwyt.bp.bean.investigate.vo.*;
import com.joysuch.wwyt.bp.constant.AppNotifyContentPre;
import com.joysuch.wwyt.bp.constant.InvestigateConfigNames;
import com.joysuch.wwyt.bp.constant.NotifyContents;
import com.joysuch.wwyt.bp.entity.BpDepart;
import com.joysuch.wwyt.bp.entity.BpInvestigateTaskAssign;
import com.joysuch.wwyt.bp.entity.BpInvestigateTaskSummary;
import com.joysuch.wwyt.bp.entity.BpJob;
import com.joysuch.wwyt.bp.entity.bean.*;
import com.joysuch.wwyt.bp.entity.investigate.*;
import com.joysuch.wwyt.bp.enums.*;
import com.joysuch.wwyt.bp.mapper.BpInvestigateTaskSummaryMapper;
import com.joysuch.wwyt.bp.mapper.InspectExceptionAlarmMesageMapper;
import com.joysuch.wwyt.bp.repository.*;
import com.joysuch.wwyt.bp.service.*;
import com.joysuch.wwyt.common.bean.KLMapping;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.common.entity.CommonBusinessNotice;
import com.joysuch.wwyt.common.enums.AppBusinessNoticeTypes;
import com.joysuch.wwyt.common.service.CommonBusinessNoticeService;
import com.joysuch.wwyt.common.service.CommonIndexNotifyService;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.*;
import com.joysuch.wwyt.core.constant.Constants;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiBusinessException;
import com.joysuch.wwyt.core.mapper.BaseUserMapper;
import com.joysuch.wwyt.core.repository.BaseTypeGroupItemDao;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.service.BizCodeGeneratorFacade;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.risk.entity.*;
import com.joysuch.wwyt.risk.repository.RiskEvaluationControlDao;
import com.joysuch.wwyt.risk.repository.RiskPointEvaluationDetailDao;
import com.joysuch.wwyt.risk.repository.RiskPointEvaluationItemDao;
import com.joysuch.wwyt.risk.service.*;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.Assert;
import org.redisson.api.RMap;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author ZhangDong
 * @Date 2020/3/25 16:22
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class BpInvestigatePlanServiceImpl implements BpInvestigatePlanService {

    @Autowired
    private BpInvestigatePlanRepository planRepository;
    @Autowired
    private BpInvestigateTypeService bpInvestigateTypeService;
    @Autowired
    private BpInvestigateRecordRepository recordRepository;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BpSafeTableItemDao itemDao;
    @Autowired
    private BpSafeTableDao tableDao;
    @Autowired
    private BpInvestigateTypeRepository typeRepository;
    @Resource
    private RedisClient redisClient;
    @Autowired
    private BpInvestigateTaskDao taskDao;
    @Autowired
    private BpInvestigateTaskService taskService;
    @Autowired
    private BpDepartDao departDao;
    @Autowired
    private BpJobDao bpJobDao;
    @Autowired
    private RiskEvaluationBusinessService riskEvaluationBusinessService;
    @Autowired
    private RiskPointEvaluationDetailDao riskPointEvaluationDetailDao;
    @Autowired
    private RiskControlLevelService riskControlLevelService;
    @Autowired
    private BpInvestigateTaskAssignService bpInvestigateTaskAssignService;
    @Autowired
    private BpInvestigateTaskSummaryService bpInvestigateTaskSummaryService;
    @Autowired
    private BpInvestigateRecordRepository investigateRecordRepository;
    @Autowired
    private BpInvestigateRecordItemDao investigateRecordItemDao;
    @Autowired
    private BpInvestigateRecordService investigateRecordService;
    @Autowired
    private RiskHiddenDangerContentService riskHiddenDangerContentService;
    @Autowired
    private BaseTypeGroupItemDao baseTypeGroupItemDao;
    @Autowired
    private InspectExceptionAlarmMesageService inspectExceptionAlarmMesageService;
    @Autowired
    private RiskPointEvaluationItemDao riskPointEvaluationItemDao;
    @Resource
    private BpInvestigateTaskSummaryMapper summaryMapper;
    @Autowired
    private CommonBusinessNoticeService commonBusinessNoticeService;
    @Autowired
    private NoticeWaitingTriggerListService noticeWaitingTriggerListService;
    @Autowired
    private InspectExceptionAlarmMesageMapper inspectExceptionAlarmMesageMapper;
    @Autowired
    private RiskPointService riskPointService;
    @Autowired
    private CommonIndexNotifyService indexNotifyService;
    @Autowired
    private AttendanceApiUserService attendanceApiUserService;
    @Autowired
    private BaseConfigService configService;
    @Autowired
    private BizCodeGeneratorFacade bizCodeGeneratorFacade;
    @Autowired
    private AuditLogProcessor auditLogProcessor;
    @Resource
    private BpDepartDao bpDepartDao;
    @Resource
    private BpInvestigateConfigService investigateConfigService;
    @Resource
    private RiskEvaluationControlDao riskEvaluationControlDao;
    @Autowired
    private RiskEvaluationTempleService riskEvaluationTempleService;
    @Autowired
    private BaseUserMapper baseUserMapper;


    @Override
    public ResultBean save(BpInvestigatePlanBean bean) {
        if (bean.getPlanEndDate() == null) {
            bean.setPlanEndDate(bean.getPlanDate());
        }
        Long id = bean.getId();
        String checkPlanName = bean.getCheckPlanName().trim();
        int count = planRepository.countByCheckPlanName(checkPlanName);

        BpInvestigatePlan bpInvestigatePlan;
        if (id == null) {
            if (count > 0) {
                return ResultBean.fail(101, "此名称已存在");
            }
            bpInvestigatePlan = new BpInvestigatePlan();
        } else {
            bpInvestigatePlan = planRepository.findById(id).orElseThrow(() -> new RuntimeException("数据不存在"));
            if (!checkPlanName.equals(bpInvestigatePlan.getCheckPlanName()) && count > 0) {
                return ResultBean.fail(101, "此名称已存在");
            }
        }
        //指定人员
        List<BpBaseUserBean> userList = bean.getUserList();
        List<BpPlanNameVO> departs = bean.getDeparts();
        List<BpPlanNameVO> jobs = bean.getJobs();
        if ((userList == null || userList.size() == 0)
                && ((departs == null || departs.size() == 0) || (jobs == null || jobs.size() == 0))) {
            throw new IllegalArgumentException("请选择排查人员或指定部门及岗位");
        }
        //时间判断
        judgeTime(bean);

        BeanUtils.copyProperties(bean, bpInvestigatePlan);
        //排查项目
        if (CollectionUtils.isEmpty(bean.getCheckItemIds())) {
            return ResultBean.fail(101, "排查项目不能为空");
        }
        List<Long> checkItemIds = getItemIds(bean);
        if (CollectionUtils.isEmpty(checkItemIds)) {
            return ResultBean.fail(101, "请确保排查项都存在");
        }
        bpInvestigatePlan.setCheckItem(JSON.toJSONString(checkItemIds));
        //排查人员
        if (userList != null && userList.size() > 0) {
            List<Long> collect = userList.stream().map(r -> r.getId()).distinct().collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                bpInvestigatePlan.setExecutor(JSON.toJSONString(collect));
            }
        }
        //异常和未检提醒人员
        List<BpBaseUserBean> remindUserList = bean.getRemindUserList();
        List<Long> remindUserIdList = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(remindUserList)) {
            remindUserIdList = remindUserList.stream().map(r -> r.getId()).collect(Collectors.toList());
        }
        bpInvestigatePlan.setRemindUserJson(JSON.toJSONString(remindUserIdList));
        // 排查人员不为空、部门和岗位为空, 此时需要清空部门和岗位
        if (!CollectionUtils.isEmpty(userList)) {
            if (CollectionUtils.isEmpty(departs)) {
                bpInvestigatePlan.setCheckDepartJson(null);
            }
            if (CollectionUtils.isEmpty(jobs)) {
                bpInvestigatePlan.setCheckJobJson(null);
            }
        } else if (!CollectionUtils.isEmpty(departs) && !CollectionUtils.isEmpty(jobs)) {
            // 排查人员为空，但部门和岗位不为空时, 需要清空排查人员
            bpInvestigatePlan.setExecutor(null);
        }

        // 排查部门
        if (bean.getDeparts() != null) {
            List<Long> departIds = bean.getDeparts().stream().map(BpPlanNameVO::getId).collect(Collectors.toList());
            bpInvestigatePlan.setCheckDepartJson(JsonUtil.toJson(departIds));
        }
        // 排查岗位
        if (bean.getJobs() != null) {
            List<Long> jobIds = bean.getJobs().stream().map(BpPlanNameVO::getId).collect(Collectors.toList());
//            bpInvestigatePlan.setCheckJobJson(JsonUtil.toJson(jobIds));
            bpInvestigatePlan.setCheckJobJson(JsonUtil.toJson(jobIds));
        }

        //周期
        List<Integer> periodList = bean.getPeriodList();
        bpInvestigatePlan.setPeriodJson(periodList == null ? null : JSON.toJSONString(periodList));
        bpInvestigatePlan.setSourceType(SourceType.OLD.getValue());
        BpInvestigatePlan plan = planRepository.save(bpInvestigatePlan);
        return ResultBean.success(plan.getId());
    }


    @Override
    public void saveBatch(InvestigatePlan plan, Boolean batchFlag) {
        List<Long> hiddenDangerContentIds = plan.getHiddenDangerContentIds();
        if (CollectionUtils.isEmpty(hiddenDangerContentIds)) {
            return;
        }
        List<Long> contentIdList = hiddenDangerContentIds.stream().distinct().collect(Collectors.toList());
        if (batchFlag && !CollectionUtils.isEmpty(contentIdList)) {
            planRepository.updateInitiateStatusByIds(contentIdList);
        }
        for (Long contentId : contentIdList) {
            List<com.joysuch.wwyt.bp.bean.investigate.PlanDetail> investigatePlans = plan.getInvestigatePlans();
            BpInvestigatePlan bpInvestigatePlan = null;
            for (PlanDetail each : investigatePlans) {
                if (each.getId() == null) {
                    bpInvestigatePlan = new BpInvestigatePlan();
                    bpInvestigatePlan.setPeriodUnit(each.getPeriodUnit());
                    bpInvestigatePlan.setPeriodDays(each.getPeriodDays());
                    bpInvestigatePlan.setPlanStartTime(each.getPlanStartTime());
                    bpInvestigatePlan.setPlanEndTime(each.getPlanEndTime());
                    bpInvestigatePlan.setCheckRateDescribe(each.getCheckRateDescribe());
                    bpInvestigatePlan.setInitiateStatus(each.getInitiateStatus());
                    bpInvestigatePlan.setTroubleshootingType(each.getTroubleshootingType());
                    bpInvestigatePlan.setFirstTaskTime(each.getFirstTaskTime());
                    if (!CollectionUtils.isEmpty(each.getCheckJobList())){
                        List<Long> jobIds = each.getCheckJobList().stream().map(JobBeanVo::getId).collect(Collectors.toList());
                        bpInvestigatePlan.setCheckJobJson(JSON.toJSONString(jobIds));
                        //取多岗位中第一个
                        bpInvestigatePlan.setCheckJob(each.getCheckJobList().get(0).getId());
                        each.setCheckJobName(each.getCheckJobList().get(0).getName());
                    }
                } else {
                    Optional<BpInvestigatePlan> optionalPlan = planRepository.findById(each.getId());
                    if (optionalPlan.isPresent()) {
                        bpInvestigatePlan = optionalPlan.get();
                        BeanUtils.copyProperties(each, bpInvestigatePlan);
                        if (PlanStatus.STOP_USE.getDesc().equals(each.getInitiateStatus())) {
                            bpInvestigatePlan.setLastProduceDate(null);
                        }
                    }
                }
                if (bpInvestigatePlan != null) {
                    //排查岗位
                    Long unitId = each.getUnitId();
                    //执行单位,人员,岗位,部门
                    Integer executiveUnit = each.getExecutiveUnit();
                    if (!CollectionUtils.isEmpty(each.getUserIdList()) || null != unitId) {
                        if (ExecutiveUnitTypes.USER.getCode().equals(executiveUnit)) {
                            //排查人员
                            List<Long> userIds = each.getUserIdList().stream().distinct().collect(Collectors.toList());
                            bpInvestigatePlan.setExecutor(JSON.toJSONString(userIds));
                        } else if (ExecutiveUnitTypes.JOB.getCode().equals(executiveUnit)) {
                            bpInvestigatePlan.setUnitId(unitId);
                        }
                    }
                    if (!CollectionUtils.isEmpty(each.getCheckJobList())){
                        List<Long> jobIds = each.getCheckJobList().stream().map(JobBeanVo::getId).collect(Collectors.toList());
                        bpInvestigatePlan.setCheckJobJson(JSON.toJSONString(jobIds));
                        //取多岗位中第一个
                        bpInvestigatePlan.setCheckJob(each.getCheckJobList().get(0).getId());
                        each.setCheckJobName(each.getCheckJobList().get(0).getName());
                    }
                    bpInvestigatePlan.setExecutiveUnit(executiveUnit);
                    List<Integer> periodList = each.getPeriodList();
                    bpInvestigatePlan.setPeriodJson(periodList == null ? null : JSON.toJSONString(periodList));
                    bpInvestigatePlan.setCheckJob(each.getCheckJobId());
                    bpInvestigatePlan.setHiddenDangerContentId(contentId);
                    bpInvestigatePlan.setRiskPointId(riskHiddenDangerContentService.getRiskPointById(contentId));
                    bpInvestigatePlan.setPointEvaluationId(riskHiddenDangerContentService.getEvaluationIdById(contentId));
                    bpInvestigatePlan.setCheckRate(PlanCheckRateType.PERIODICITY.getDesc());
                    String checkPlanName = each.getRiskPointName() + each.getCheckJobName();
                    bpInvestigatePlan.setCheckPlanName(checkPlanName);
                    //获取排查类型，为空则新增默认类型
                    BpInvestigateType bpInvestigateType = bpInvestigateTypeService.findFirstByCheckType(Constants.RISK_CONTROL_CHECK_TYPE_DEFAULT);
                    if (bpInvestigateType == null) {
                        bpInvestigateType = saveInvestigateType();
                    }
                    bpInvestigatePlan.setTypeId(bpInvestigateType.getId());
                    bpInvestigatePlan.setTypeName(Constants.RISK_CONTROL_CHECK_TYPE_DEFAULT);
                    if (DeleteFlag.EFFECTIVE.getCode().equals(each.getDeleteFlag()) || StringUtils.isEmpty(each.getDeleteFlag())) {
                        bpInvestigatePlan.setInitiateStatus(each.getInitiateStatus());
                    } else {
                        bpInvestigatePlan.setInitiateStatus(PlanStatus.DISABLE.getDesc());
                    }
                    bpInvestigatePlan.setSourceType(SourceType.NEW.getValue());
                    bpInvestigatePlan.setTroubleshootingType(each.getTroubleshootingType());
                    planRepository.save(bpInvestigatePlan);
                }
            }
            //保存排查内容的取数来源
            RiskHiddenDangerContent hiddenDangerContent = riskHiddenDangerContentService.getById(contentId);
            hiddenDangerContent.setDateSource(plan.getDateSource());
            hiddenDangerContent.setIsJobTask(plan.getIsJobTask());
            riskHiddenDangerContentService.saveOrUpdate(hiddenDangerContent);
        }
    }

    @Override
    public InvestigatePlanVO getByContentIds(List<Long> dangerContentIds, List<Long> detailIds) {
        List<PlanDetailVO> detailVOS = new ArrayList<>();
        List<BpInvestigatePlan> plans = planRepository.findByHiddenDangerContentIds(dangerContentIds);
        plans.forEach(each -> {
            PlanDetailVO plan = new PlanDetailVO();
            BeanUtils.copyProperties(each, plan);
            //排查人员
            String executor = each.getExecutor();
            //排查岗位
            Long unitId = each.getUnitId();
            //按照选择类型分为岗位和人员  0: 人员 , 1: 岗位 ,2：部门   部门暂时未做
            //执行单位,人员,岗位
            Integer executiveUnit = each.getExecutiveUnit();
            if (!StringUtils.isEmpty(executor) || null != unitId) {
                if (ExecutiveUnitTypes.USER.getCode().equals(executiveUnit)) {
//                    List<BpBaseUserBean> userBeanList = getBpBaseUserBeans(executor);
                    List<BpBaseUserJobBean> userJobBeanList = getUserJobInfo(executor);
                    plan.setUserList(userJobBeanList);
                    plan.setCheckJobId(each.getCheckJob());
                    String jobName = bpJobDao.getNameById(each.getCheckJob());
                    plan.setCheckJobName(jobName);
                } else if (ExecutiveUnitTypes.JOB.getCode().equals(executiveUnit)) {
                    String jobName = bpJobDao.findNameById(unitId);
                    if (org.apache.commons.lang3.StringUtils.isNotBlank(jobName)) {
                        BpBaseJobBean bpBaseJobBean = new BpBaseJobBean(unitId, jobName);
                        plan.setBpBaseJobBean(bpBaseJobBean);
                    }
                }
                if (each.getTroubleshootingType() != null) {
                    plan.setTroubleshootingType(each.getTroubleshootingType());
                    plan.setTroubleshootingTypeName(baseTypeGroupItemDao.findNameById(each.getTroubleshootingType()));
                    plan.setExecutiveUnit(executiveUnit);
                    List<Integer> periodList = (JSON.parseArray(each.getPeriodJson(), Integer.class));
                    plan.setPeriodList(periodList);
                    plan.setInitiateStatus(each.getInitiateStatus());
                }
                if (!StringUtils.isEmpty(each.getCheckJobJson())){
                    List<Long> jobIds = JsonUtil.fromListJson(each.getCheckJobJson(), Long.class);
                    List<JobBeanVo> jobList = bpJobDao.findAllByIdInfo(jobIds).stream().map(job -> {
                        JobBeanVo vo = new JobBeanVo();
                        vo.setId(job.getId());
                        vo.setName(job.getName());
                        return vo;
                    }).collect(Collectors.toList());
                    plan.setCheckJobList(jobList);
                }
            }
            detailVOS.add(plan);
        });
        List<RiskPointEvaluationDetail> detailList = riskPointEvaluationDetailDao.findByDetailIds(detailIds);
        List<ControlLevelVO> controlLevels = new ArrayList<>();
        detailList.forEach(each -> {
            ControlLevelVO controlLevelVO = new ControlLevelVO();
            controlLevelVO.setMeasureId(each.getItemId());
            controlLevelVO.setMeasureContent(each.getContent());
            String responseDepartIds = each.getResponseDepartIds();
            if (StringUtil.isNotBlank(responseDepartIds)) {
                List<KLMapping> departList = builderDepartList(responseDepartIds);
                controlLevelVO.setResponseDepart(departList);
            }
            String controlLevelIds = each.getControlLevelIds();
            if (StringUtil.isNotBlank(controlLevelIds)) {
                List<KLMapping> levelList = builderControlLevelList(controlLevelIds);
                controlLevelVO.setControlLevel(levelList);
            }
            controlLevels.add(controlLevelVO);
        });
        //取数来源
        Long dateSource = null;
        String dateSourceName = null;
        Integer isJobTask = null;
        //排查类型
        RiskHiddenDangerContent dangerContent = riskHiddenDangerContentService.getById(dangerContentIds.get(0));
        if (dangerContent != null) {
            dateSource = dangerContent.getDateSource();
            isJobTask = dangerContent.getIsJobTask();
            dateSourceName = baseTypeGroupItemDao.findNameById(dateSource);
        }
        return InvestigatePlanVO.builder().planDetails(detailVOS).controlLevels(controlLevels).dateSource(dateSource).
                dateSourceName(dateSourceName).isJobTask(isJobTask).build();
    }

    private List<BpBaseUserJobBean> getUserJobInfo(String remindUserJson) {
        List<Long> remindUserIdList = JSONArray.parseArray(remindUserJson, Long.class);
        List<BpBaseUserJobBean> userJobBeanList = new ArrayList<>(remindUserIdList.size());
        if (!CollectionUtils.isEmpty(remindUserIdList)) {
            userJobBeanList = baseUserMapper.getUserJobBeanList(remindUserIdList);
        }
        return userJobBeanList;
    }

    private List<BpBaseUserBean> getUserByDepartAndJob(String checkDepartJson, String checkJobJson) {
        List<BpBaseUserBean> result = Lists.newArrayList();
        if (org.apache.commons.lang3.StringUtils.isEmpty(checkDepartJson)) {
            return result;
        }
        if (org.apache.commons.lang3.StringUtils.isEmpty(checkJobJson)) {
            return result;
        }
        List<Long> departIds = JsonUtil.fromListJson(checkDepartJson, Long.class);
        List<Long> jobIds = JsonUtil.fromListJson(checkJobJson, Long.class);
        if (departIds.size() == 0 || jobIds.size() == 0) {
            return result;
        }
        List<BaseUser> users = baseUserDao.findByDepartIdsAndJobIds(departIds, jobIds);

        users.forEach(user -> {
            BpBaseUserBean userBean = new BpBaseUserBean();
            userBean.setId(user.getId());
            userBean.setName(user.getRealName());
            result.add(userBean);
        });
        return result;
    }

    @Override
    public void produceTaskForNew() {
        produceTask();
    }

    @Override
    public void deleteByHiddenDangerContentIds(List<Long> ids) {
        planRepository.deleteByHiddenDangerContentIds(ids);
    }

    @Override
    public void updateInitiateStatusByIds(List<Long> ids) {
        planRepository.updateInitiateStatusByIds(ids);
    }

    private List<KLMapping> builderDepartList(String responseDepartIds) {
        List<Long> departIds = Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(responseDepartIds)
                .stream().map(id -> Long.parseLong(id.trim())).collect(Collectors.toList());
        List<KLMapping> departList = new ArrayList<>();
        departIds.forEach(id -> {
            KLMapping klMapping = new KLMapping();
            String departName = riskEvaluationBusinessService.getDepartName(id);
            klMapping.setId(id);
            klMapping.setValue(departName);
            departList.add(klMapping);
        });
        return departList;
    }

    private List<KLMapping> builderControlLevelList(String controlLevelIds) {
        List<Long> levelIds = Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(controlLevelIds)
                .stream().map(id -> Long.parseLong(id.trim())).collect(Collectors.toList());
        List<KLMapping> levelList = new ArrayList<>();
        levelIds.forEach(id -> {
            KLMapping klMapping = new KLMapping();
            RiskControllevels level = riskControlLevelService.getByCode(id);
            klMapping.setId(id);
            klMapping.setValue(level.getName());
            levelList.add(klMapping);
        });
        return levelList;
    }

    @Override
    public BpInvestigateType saveInvestigateType() {
        BpInvestigateType investigateType = new BpInvestigateType();
        investigateType.setCheckType(Constants.RISK_CONTROL_CHECK_TYPE_DEFAULT);
        return typeRepository.save(investigateType);
    }

    private List<Long> getItemIds(BpInvestigatePlanBean bean) {
        //排除数据表已删除的项
        List<Long> checkItemIds = bean.getCheckItemIds();
        List<BpSafeTableItem> itemList = itemDao.findAllById(checkItemIds);
        if (checkItemIds.size() != itemList.size()) {
            HashSet<Long> totalSet = new HashSet<>(checkItemIds);
            Set<Long> tableSet = itemList.stream().map(r -> r.getId()).collect(Collectors.toSet());
            Sets.SetView<Long> set = Sets.intersection(totalSet, tableSet);
            checkItemIds = Lists.newArrayList(set);
        }
        return checkItemIds;
    }

    private void judgeTime(BpInvestigatePlanBean bean) {
        String checkRate = bean.getCheckRate();
        String periodUnit = bean.getPeriodUnit();
        Integer periodDays = bean.getPeriodDays();
        Integer monthStartDate = bean.getMonthStartDate();

        Assert.isTrue("临时性".equals(checkRate) || "周期性".equals(checkRate), "不支持此排查频率");
        if ("临时性".equals(checkRate)) {
            Assert.notNull(bean.getPlanDate(), "请设置开始时间");
            Assert.notNull(bean.getPlanEndDate(), "请设置结束时间");
        } else {
            Assert.isTrue("日".equals(periodUnit) || "月".equals(periodUnit) || "每几月".equals(periodUnit), "请设置周期");
            List<Integer> periodList = bean.getPeriodList();
            //日
            if ("日".equals(periodUnit)) {
                //周期单位为日时，按天/按周二选一
                Assert.isTrue(periodDays != null || !CollectionUtils.isEmpty(periodList), "请选择按天/按周");
                if (periodDays != null) {
                    Assert.isTrue(periodDays >= 1, "请填写正确的天数");
                }
                if (!CollectionUtils.isEmpty(periodList)) {
                    //二选一
                    if (periodDays != null) {
                        bean.setPeriodList(null);
                    } else {
                        for (Integer week : periodList) {
                            if (week < 1 || week > 7) {
                                throw new IllegalArgumentException("按周传值错误");
                            }
                        }
                    }
                }
            }
            //月
            else if ("月".equals(periodUnit)) {
                //周期单位为月时，周期和日期必填
                Assert.isTrue(periodDays != null && !CollectionUtils.isEmpty(periodList), "周期和日期必填");
                Assert.isTrue(periodDays >= 1, "请填写正确的月数");
                for (Integer month : periodList) {
                    if (month < 1 || month > 31) {
                        throw new IllegalArgumentException("日期传值错误");
                    }
                }
            }
            //每几月
            else {
                //周期单位为月时，周期和日期必填
                Assert.isTrue(periodDays != null && monthStartDate != null, "周期和起始月份必填");
                Assert.isTrue(periodDays >= 1, "请填写正确的月数");
            }
        }
    }

    @Override
    public ResultBean delete(Long id) {
        Assert.notNull(id, "id cannot be null");
        planRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("此数据不存在"));
        List<BpInvestigateRecord> recordList = recordRepository.findByCheckPlanId(id);
        if (!CollectionUtils.isEmpty(recordList)) {
            return ResultBean.fail(101, "该排查计划已被排查记录绑定，请先解除绑定再删除");
        }
        //计划删除，同时删除所有任务
        List<BpInvestigateTask> taskList = taskDao.findByPlanId(id);
        if (!CollectionUtils.isEmpty(taskList)) {
            List<Long> taskIdList = taskList.stream().map(r -> r.getId()).collect(Collectors.toList());
            taskDao.deleteTasks(taskIdList);
        }

        planRepository.deleteById(id);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean page(BpInvestigatePlanQueryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        String checkPlanName = bean.getCheckPlanName();
        Long typeId = bean.getTypeId();
        Specification<BpInvestigatePlan> specification = (Root<BpInvestigatePlan> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("sourceType"), SourceType.OLD.getValue()));
            if (!StringUtils.isEmpty(checkPlanName)) {
                predicates.add(cb.like(root.get("checkPlanName"), "%" + checkPlanName + "%"));
            }
            if (typeId != null) {
                predicates.add(cb.equal(root.get("typeId"), typeId));
            }
            if (bean.getInitiateStatus() != null) {
                predicates.add(cb.equal(root.get("initiateStatus"), bean.getInitiateStatus()));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<BpInvestigatePlan> page = planRepository.findAll(specification, pageable);
        List<BpInvestigatePlan> content = page.getContent();
        Map<Long, String> typeMap = getTypeMap(content);
        List<BpInvestigatePlanBean> list = new ArrayList<>(content.size());
        for (BpInvestigatePlan plan : page) {
            BpInvestigatePlanBean planBean = new BpInvestigatePlanBean();
            BeanUtils.copyProperties(plan, planBean);
            planBean.setTypeName(typeMap.getOrDefault(plan.getTypeId(), ""));
            //排查人员
            String executor = plan.getExecutor();
            if (!StringUtils.isEmpty(executor)) {
                List<BpBaseUserBean> userBeanList = getBpBaseUserBeans(executor);
                planBean.setUserList(userBeanList);
            }
            if (!StringUtils.isEmpty(plan.getCheckJobJson())) {
                List<Long> jobIds = JsonUtil.fromListJson(plan.getCheckJobJson(), Long.class);
                if (!CollectionUtils.isEmpty(jobIds)) {
                    List<JobBeanVo> jobList = bpJobDao.findAllByIdInfo(jobIds).stream().map(job -> {
                        JobBeanVo vo = new JobBeanVo();
                        vo.setId(job.getId());
                        vo.setName(job.getName());
                        return vo;
                    }).collect(Collectors.toList());
                    planBean.setCheckJobList(jobList);
                }
            }
            list.add(planBean);
        }
        return ResultBean.pageData(list, page.getTotalElements());
    }

    private Map<Long, String> getTypeMap(List<BpInvestigatePlan> content) {
        List<Long> typeIdList = content.stream().map(r -> r.getTypeId()).distinct().collect(Collectors.toList());
        return typeRepository.findAllById(typeIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getCheckType()));
    }

    //flag: 0 web; 1 app
    @Override
    public ResultBean getById(Long id, Integer flag) {
        BpInvestigatePlan bpInvestigatePlan = planRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("数据不存在"));
        BpInvestigatePlanBean bean = new BpInvestigatePlanBean();
        BeanUtils.copyProperties(bpInvestigatePlan, bean);
        convert(flag, bpInvestigatePlan, bean);
        return ResultBean.success(bean);
    }

    @Override
    public ResultBean getPlanById(Long id, Integer flag) {
        BpInvestigatePlan bpInvestigatePlan = planRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("关联的计划已被删除，任务无法执行."));
        BpInvestigatePlanBean bean = new BpInvestigatePlanBean();
        BeanUtils.copyProperties(bpInvestigatePlan, bean);
        convert(flag, bpInvestigatePlan, bean);
        return ResultBean.success(bean);
    }

    /**
     * 排查计划补全: 排查类型; 周期设置; 排查项目; 排查表名称; 责任单位; 排查人员; 异常通知人员
     *
     * @param flag 0 web; 1 app
     */
    private void convert(Integer flag, BpInvestigatePlan bpInvestigatePlan, BpInvestigatePlanBean bean) {
        //排查类型
        BpInvestigateType type = bpInvestigateTypeService.getById(bpInvestigatePlan.getTypeId());
        bean.setTypeName(type.getCheckType());
        //周期设置
        String periodJson = bpInvestigatePlan.getPeriodJson();
        if (!StringUtils.isEmpty(periodJson)) {
            bean.setPeriodList(JSON.parseArray(periodJson, Integer.class));
        }
        //排查项目
        String checkItem = bpInvestigatePlan.getCheckItem();
        if (!StringUtils.isEmpty(checkItem)) {
            List<Long> checkItemIdList = JSONArray.parseArray(checkItem, Long.class);
            List<BpSafeTableItemBean> itemBeanList = new ArrayList<>(checkItemIdList.size());
            if (!CollectionUtils.isEmpty(checkItemIdList)) {
                List<BpSafeTableItem> itemList = itemDao.findAllById(checkItemIdList);
                itemList.forEach(r -> {
                    BpSafeTableItemBean itemBean = new BpSafeTableItemBean();
                    BeanUtils.copyProperties(r, itemBean);
                    itemBeanList.add(itemBean);
                });
                if (flag == 1) {
                    Map<String, List<BpSafeTableItem>> itemMap = itemList.stream().collect(Collectors.groupingBy(r -> r.getItem()));
                    List<AppInvestigateDto> appItemBeanList = Lists.newArrayList();
                    for (String key : itemMap.keySet()) {
                        List<BpSafeTableItem> itemTempList = itemMap.get(key);
                        appItemBeanList.add(new AppInvestigateDto(key, itemTempList.size(), itemTempList));
                    }
                    appItemBeanList.sort(Comparator.comparing(AppInvestigateDto::getCheckItemSum).reversed());
                    bean.setAppItemBeanList(appItemBeanList);
                }
            }
            if (flag == 0) {
                bean.setItemBeanList(itemBeanList);
            }
        }

        //排查表名称
        Long safeTableId = bpInvestigatePlan.getSafeTableId();
        if (safeTableId != null) {
            tableDao.findById(safeTableId).ifPresent(r -> {
                bean.setSafeTableName(r.getTableName());
                //安全表责任部门
                Long dutyDepartId = r.getDutyDepartId();
                if (dutyDepartId != null) {
                    departDao.findById(dutyDepartId).ifPresent(v -> {
                        bean.setDutyDepartId(dutyDepartId);
                        bean.setDutyDepartName(v.getName());
                    });
                }
            });
        }
        //排查人员
        String executor = bpInvestigatePlan.getExecutor();
        if (!StringUtils.isEmpty(executor)) {
            List<BpBaseUserBean> userBeanList = getBpBaseUserBeans(executor);
            bean.setUserList(userBeanList);
        }
        if (!StringUtils.isEmpty(bpInvestigatePlan.getCheckJobJson())){
            List<Long> jobIds = JsonUtil.fromListJson(bpInvestigatePlan.getCheckJobJson(), Long.class);
            List<JobBeanVo> jobList = bpJobDao.findAllByIdInfo(jobIds).stream().map(job -> {
                JobBeanVo vo = new JobBeanVo();
                vo.setId(job.getId());
                vo.setName(job.getName());
                return vo;
            }).collect(Collectors.toList());
            bean.setCheckJobList(jobList);
        }
        //异常通知人员
        String remindUserJson = bpInvestigatePlan.getRemindUserJson();
        if (!StringUtils.isEmpty(remindUserJson)) {
            List<BpBaseUserBean> remindUserBeanList = getBpBaseUserBeans(remindUserJson);
            bean.setRemindUserList(remindUserBeanList);
        }
        // 部门
        if (!org.apache.commons.lang3.StringUtils.isEmpty(bpInvestigatePlan.getCheckDepartJson())) {
            List<Long> departIds = JsonUtil.fromListJson(bpInvestigatePlan.getCheckDepartJson(), Long.class);
            if (departIds != null && departIds.size() > 0) {
                List<BpDepart> departs = bpDepartDao.findByIdIn(departIds);
                List<BpPlanNameVO> departList = Lists.newArrayList();
                departs.forEach(depart -> {
                    BpPlanNameVO vo = new BpPlanNameVO();
                    vo.setId(depart.getId());
                    vo.setName(depart.getName());
                    departList.add(vo);
                });
                bean.setDeparts(departList);
            }
        }
        // 岗位
        if (!org.apache.commons.lang3.StringUtils.isEmpty(bpInvestigatePlan.getCheckJobJson())) {
            List<Long> jobIds = JsonUtil.fromListJson(bpInvestigatePlan.getCheckJobJson(), Long.class);
            if (jobIds != null && jobIds.size() > 0) {
                List<BpJob> bpJobList = bpJobDao.findAllByIdInfo(jobIds);
                List<BpPlanNameVO> jobList = Lists.newArrayList();
                bpJobList.forEach(job -> {
                    BpPlanNameVO vo = new BpPlanNameVO();
                    vo.setId(job.getId());
                    vo.setName(job.getName());
                    jobList.add(vo);
                });
                bean.setJobs(jobList);
            }
        }

    }

    /**
     * 定时生成排查任务
     */
    @Scheduled(cron = "0 5 0 * * ?")
    public synchronized void produce() {
        produceTaskForOld();
        produceTask();
    }

    /**
     * 定时删除禁用的计划生成的任务（每天零点十分执行一次）
     */
    @Scheduled(cron = "0 30 0 * * ?")
    public void cleanUselessTask() {
        // 清除无用(计划已禁用|隐患被删除|评估详情被删除|风险点被删除)任务
        planRepository.cleanUselessTask();
    }

    @Override
    public void produceTaskForOld() {
        log.info("investigate schedule produce task begin...");
        List<BpInvestigatePlan> list = planRepository.findByInitiateStatusAndSourceType(PlanStatus.ENABLE.getDesc(), SourceType.OLD.getValue());
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        Map<Long, BpInvestigatePlan> planMap = list.stream().collect(Collectors.toMap(BpInvestigatePlan::getId, e -> e));
        // 个推配置
        boolean needPush = false;
        BaseConfig config = configService.findFirstConfig(BaseConfigCodes.INVESTIGATE_GETUI_ON_OFF);
        needPush = null != config && Constant.COMMON_SWITCH_ON.equals(config.getValue());
        //加入开关
        boolean generateCheck = false;
        String generateStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.TASK_GENERATION_NOTIFICATION);
        generateCheck = org.apache.commons.lang3.StringUtils.isNotBlank(generateStatus) && Constant.COMMON_SWITCH_ON.equals(generateStatus);
        List<BpInvestigateTask> taskList = new ArrayList<>();
        for (BpInvestigatePlan plan : list) {
            Date lastProduceDate = plan.getLastProduceDate();
            //判断今天是否需要生成任务
            if (needProduceTask(plan, lastProduceDate)) {
                log.info("investigate schedule produce task，check no need produce task...");
                continue;
            }
            String executor = plan.getExecutor();
            if (StringUtils.isEmpty(executor)) {
                continue;
            }
            List<Long> userIdList = JSON.parseArray(executor, Long.class);
            userIdList = userIdList.stream().distinct().collect(Collectors.toList());
            String taskKey = getInvestigatePlanKey(plan);
            if (CollectionUtils.isEmpty(userIdList)) {
                continue;
            }
            List<BpBaseUserBean> userBeanList = getUserByDepartAndJob(plan.getCheckDepartJson(), plan.getCheckJobJson());
            if (userBeanList != null && userBeanList.size() > 0) {
                for (BpBaseUserBean userBean : userBeanList) {
                    if (!userIdList.contains(userBean.getId())) {
                        userIdList.add(userBean.getId());
                    }
                }
            }

            int taskFlag = 0;
            Date currDate = DateUtil.beginOfDay(new Date());
            for (Long userId : userIdList) {
                String exist = redisClient.get(taskKey + userId);
                //命中缓存pass
                if (exist != null) {
                    continue;
                }
                taskFlag = 1;
                BpInvestigateTask task = taskDao.save(newTask(plan, userId, currDate));
                if (task != null) {
                    newTaskAssign(task);
                    taskList.add(task);
                }
                generateTaskAppNotifyForOld(userId, plan.getCheckRateDescribe(), plan.getCheckRate(), plan.getCheckPlanName(), task, needPush,generateCheck);
                redisClient.setEx(taskKey + userId, "exist", 2, TimeUnit.DAYS);
            }
            if (taskFlag == 1) {
                plan.setLastProduceDate(DateUtil.date());
                planRepository.save(plan);
            }
        }
        // 生成汇总
        if (CollectionUtil.isNotEmpty(taskList)) {
            List<BpInvestigateTaskSummary> summaryList = new ArrayList<>();
            taskList.forEach(task -> {
                BpInvestigatePlan plan = planMap.get(task.getPlanId());
                BpInvestigateTaskSummary taskSummary = new BpInvestigateTaskSummary();
                taskSummary.setId(YitIdHelper.nextId());
                taskSummary.setCheckName(plan == null ? "" : plan.getCheckPlanName());
                taskSummary.setCheckType(plan == null ? "" : plan.getTypeName());
                taskSummary.setCheckDate(task.getCheckDate());
                taskSummary.setCheckStatus(task.getStatus());
                taskSummary.setOrgCode(task.getOrgCode());
                taskSummary.setTenentId(task.getTenentId());
                taskSummary.setUserId(task.getUserId());
                taskSummary.setSourceType(SourceType.OLD.getValue());
                taskSummary.setTaskCount(1);
                taskSummary.setTaskId(task.getId());
                // 排查表生成的summary不允许删除
                taskSummary.setHasProcessRecord(true);
                summaryList.add(taskSummary);
            });
            if (CollectionUtil.isNotEmpty(summaryList)) {
                bpInvestigateTaskSummaryService.saveBatch(summaryList);
            }
        }

        log.info("investigate schedule produce task end...");
    }

    public void produceTask() {
        log.info("investigate schedule produce task begin...");
        // 生成任务之前先处理一次未检任务，防止误删汇总
        taskTimeOutCheck();
        // 旧任务，风险点、评估、隐患被删除的，周期设置为0的，禁用的，排除掉，不生成
        List<BpInvestigatePlan> toProducePlanList = planRepository.findNewPlanToProduce();
        if (CollectionUtil.isEmpty(toProducePlanList)) {
            return;
        }
        Set<Long> allExecutor = new HashSet<>();
        // 准备生成汇总和任务需要的数据
        Map<Long, BpInvestigatePlan> planMap = toProducePlanList.stream().collect(Collectors.toMap(BpInvestigatePlan::getId, r -> r));
        Map<Long, List<Long>> planExecutor = preparePlanExecutor(toProducePlanList, allExecutor);
        // 任务执行人为空, 则不生成任务
        if (CollectionUtil.isEmpty(allExecutor)) {
            return;
        }
        // 风险点，id-name映射
        Set<Long> riskPointSet = toProducePlanList.stream().map(BpInvestigatePlan::getRiskPointId).collect(Collectors.toSet());
        List<RiskPoint> riskPoints = riskPointService.findByIdInIgnoreDeleteStatus(riskPointSet);
        Map<Long, String> pointIdNameMapping = riskPoints.stream().collect(Collectors.toMap(RiskPoint::getId, RiskPoint::getName));
        // 计划风险点-风险区域映射
        Map<Long, RiskDistrict> pointDistrictMap = riskPointService.findPointDistrictMap(riskPointSet);
        // 个推配置
        boolean needPush = false;
        BaseConfig config = configService.findFirstConfig(BaseConfigCodes.INVESTIGATE_GETUI_ON_OFF);
        needPush = null != config && Constant.COMMON_SWITCH_ON.equals(config.getValue());
        //即将过期开关
        boolean expireCheck = false;
        String expireStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.NOTICE_ABOUT_TO_EXPIRE);
        expireCheck = org.apache.commons.lang3.StringUtils.isNotBlank(expireStatus) && Constant.COMMON_SWITCH_ON.equals(expireStatus);
        //任务生成通知开关
        boolean generateCheck = false;
        String generateStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.TASK_GENERATION_NOTIFICATION);
        generateCheck = org.apache.commons.lang3.StringUtils.isNotBlank(generateStatus) && Constant.COMMON_SWITCH_ON.equals(generateStatus);
        // 生成汇总
        List<BpInvestigateTaskSummary> todayGenUserSummaryList = generateSummary(allExecutor);
        Map<Long, BpInvestigateTaskSummary> todayGenUserSummaryMap = todayGenUserSummaryList.stream().collect(Collectors.toMap(BpInvestigateTaskSummary::getUserId, e -> e));
        // 此次生成的任务
        final List<BpInvestigateTask> taskList = new ArrayList<>();
        final List<BpInvestigateTaskAssign> taskAssignList = new ArrayList<>();
        // 生成任务个推
        final NotifyListsBean notifys = new NotifyListsBean();

        ArrayList<BpInvestigatePlan> hadProduceplan = new ArrayList<>();
        for (BpInvestigatePlan plan : toProducePlanList) {
            // 一条不正确的计划，不应影响其他计划的生成
            try {
                List<Long> executor = planExecutor.get(plan.getId());
                if (!CollectionUtils.isEmpty(executor)) {
                    GeneratePlanTuple tuple = generateTaskByPlan(plan, taskList, pointDistrictMap);
                    if (tuple.getGenerateFlag()) {
                        // 第一次生成，lastProduceDate设置为计划开始时间，针对如七号新增一号开始的计划。(周、日任务无开始时间，时任务直接设置当前时间)
                        if (plan.getLastProduceDate() == null && !CycleType.W.getValue().equals(plan.getPeriodUnit()) && !CycleType.D.getValue().equals(plan.getPeriodUnit()) && !CycleType.H.getValue().equals(plan.getPeriodUnit())) {
                            plan.setLastProduceDate(plan.getPlanStartTime());
                        } else {
                            plan.setLastProduceDate(tuple.getLastProduceDate());
                        }
                        hadProduceplan.add(plan);
                    }
                }
            } catch (Exception e) {
                log.error("隐患排查任务生成出错：计划id{}-{}", plan.getId(), e);
            }
        }
        if (CollectionUtil.isNotEmpty(hadProduceplan)) {
            planRepository.saveAll(hadProduceplan);
        }
        // taskService.batchSave(taskList,1000);
        taskService.saveBatch(taskList);
        if (CollectionUtil.isNotEmpty(taskList)) {
            boolean finalNeedPush = needPush;
            // 准备assign数据，通过排班模块过滤
            List<TaskForAssignBean> assignBeans = taskList.stream().parallel()
                    .map(task -> new TaskForAssignBean(
                                    task.getId(),
                                    task.getCheckDate(),
                                    planExecutor.get(task.getPlanId())
                            )
                    ).collect(Collectors.toList());
            // 排班过滤分配人
            assignBeans = attendanceApiUserService.batchUserTaskCheck(assignBeans);
            Map<Long, List<Long>> taskAssignMap = assignBeans.stream().parallel()
                    .collect(Collectors.toMap(TaskForAssignBean::getTaskId, TaskForAssignBean::getAssignList));
            boolean finalExpireCheck = expireCheck;
            boolean finalGenerateCheck = generateCheck;
            taskList.forEach(task -> {
                BpInvestigatePlan plan = planMap.get(task.getPlanId());
                // assign
                List<Long> assignUserIds = taskAssignMap.get(task.getId());
                if (CollectionUtil.isNotEmpty(assignUserIds)) {
                    assignUserIds.forEach(userId -> {
                        BpInvestigateTaskAssign assign = new BpInvestigateTaskAssign();
                        BpInvestigateTaskSummary summary = todayGenUserSummaryMap.get(userId);
                        if (null != summary) {
                            summary.setTaskCount(summary.getTaskCount() + 1);
                            // 今日有任务生成的汇总不允许删除
                            summary.setHasProcessRecord(Boolean.TRUE);
                            assign.setSummaryId(summary.getId());
                        }
                        assign.setAssignUser(userId);
                        assign.setTaskId(task.getId());
                        assign.setTenentId(task.getTenentId());
                        assign.setOrgCode(task.getOrgCode());
                        taskAssignList.add(assign);
                    });
                }
                // notify
                generateTaskAppNotifyForNew(assignUserIds, pointIdNameMapping.get(plan.getRiskPointId()), task, notifys, finalNeedPush, finalGenerateCheck);
                addTaskComingDeadNotify(task, assignUserIds, notifys, finalNeedPush,finalExpireCheck);
            });
            // 保存assign
            if (CollectionUtil.isNotEmpty(taskAssignList)) {
                List<List<BpInvestigateTaskAssign>> taskPartition = Lists.partition(taskAssignList,1000);
                for (List<BpInvestigateTaskAssign> list : taskPartition){
                    list.forEach(taskAssign->taskAssign.setId(YitIdHelper.nextId()));
                    bpInvestigateTaskAssignService.saveBatch(list);
                }
            }
            if (CollectionUtil.isNotEmpty(todayGenUserSummaryList)) {
                bpInvestigateTaskSummaryService.saveOrUpdateBatch(todayGenUserSummaryList);
            }
            // 保存通知
            saveNotifys(notifys);
        }
        // 任务生成结束，检查一次，将隐患中的计划生成的任务刷成正常(隐患整改)
        taskTimeOutCheck();
        log.info("investigate schedule produce task end...");
    }

    @Scheduled(cron = "0 0 1 * * ?")
    private void deleteUselessSummaryByDate() {
        // 删除今天之前的无用汇总
        log.info("delete useless summary start");
        summaryMapper.deleteUselessSummaryByDate(DateUtil.beginOfDay(DateUtil.date()));
        log.info("delete useless summary end");
    }

    /**
     * 保存通知提醒（app通知中心、app个推、web首页）
     *
     * @param notifys
     */
    void saveNotifys(NotifyListsBean notifys) {
        if (CollectionUtil.isNotEmpty(notifys.getGeTuiNotifyList())) {
            try {
                noticeWaitingTriggerListService.saveBatch(notifys.getGeTuiNotifyList());
            } catch (Exception e) {
                log.error("隐患排查任务生成个推保存失败！{}", e);
            }
        }
        if (CollectionUtil.isNotEmpty(notifys.getCommonBusinessNotifyList())) {
            try {
                commonBusinessNoticeService.saveBatch(notifys.getCommonBusinessNotifyList());
            } catch (Exception e) {
                log.error("隐患排查任务生成app通知保存失败！{}", e);
            }
        }
        if (CollectionUtil.isNotEmpty(notifys.getCommonIndexNotifyList())) {
            try {
                indexNotifyService.saveList(notifys.getCommonIndexNotifyList());
            } catch (Exception e) {
                log.error("隐患排查任务生成web通知保存失败！{}", e);
            }
        }
    }


    /**
     * 获取计划和计划执行人的映射，并将所有执行人全部取出，准备生成汇总
     *
     * @param toProducePlanList
     * @param allExecutor
     * @return
     */
    private Map<Long, List<Long>> preparePlanExecutor(List<BpInvestigatePlan> toProducePlanList, Set<Long> allExecutor) {
        Map<Long, List<Long>> planExecutor = new HashMap<>();
        Set<Long> jobs = new HashSet<>();
        List<BpInvestigatePlan> jobPlan = new ArrayList<>();
        toProducePlanList.forEach(each -> {
            List<Long> executors = null;
            //人员与岗位的生成计划分开处理
            if (ExecutiveUnitTypes.USER.getCode().equals(each.getExecutiveUnit())) {
                // 直接取executor
                List<Long> executorIdList = JSON.parseArray(each.getExecutor(), Long.class);
                allExecutor.addAll(executorIdList);
                planExecutor.put(each.getId(), executorIdList);
            } else if (ExecutiveUnitTypes.JOB.getCode().equals(each.getExecutiveUnit())) {
                // 先保存岗位ID和计划，在做处理
                jobs.add(each.getUnitId());
                jobPlan.add(each);
            }
        });
        if (CollectionUtil.isNotEmpty(jobs)) {
            // 根据岗位取出用户id后，按岗位分组
            List<BaseUser> userAndJob = baseUserDao.findByJobs(jobs);
            Map<Long, List<BaseUser>> jobUserMapping = userAndJob.stream().collect(Collectors.groupingBy(BaseUser::getJob));
            jobPlan.forEach(each -> {
                List<BaseUser> users = jobUserMapping.get(each.getUnitId());
                if (CollectionUtil.isNotEmpty(users)) {
                    List<Long> userIds = users.stream().map(BaseUser::getId).collect(Collectors.toList());
                    allExecutor.addAll(userIds);
                    planExecutor.put(each.getId(), userIds);
                }
            });
        }
        return planExecutor;
    }

    /**
     * 生成汇总
     *
     * @param allExecutorIds
     * @return
     */
    private List<BpInvestigateTaskSummary> generateSummary(Set<Long> allExecutorIds) {
        if (CollectionUtil.isEmpty(allExecutorIds)) {
            return Lists.newArrayList();
        }
        // 此次生成的汇总。userId : summaryId
        List<BpInvestigateTaskSummary> todayGenUserSummaryList = new ArrayList<>();
        List<BaseUser> allExecutors = baseUserDao.getAllByIdsWithOutStatus(allExecutorIds);
        // 此次生成之前，今日已存在的汇总
        List<BpInvestigateTaskSummary> todayContainsSummary = summaryMapper.findSummaryByDate(DateUtil.beginOfDay(DateUtil.date()));
        Map<Long, BpInvestigateTaskSummary> todayContainsSummaryMap = new HashMap<>();
        todayContainsSummary.forEach(each -> todayContainsSummaryMap.put(each.getUserId(), each));
        if (!CollectionUtils.isEmpty(allExecutors)) {
            allExecutors.forEach(each -> {
                if (!todayContainsSummaryMap.containsKey(each.getId())) {
                    // 今日已存在此人的汇总，跳过不生成
                    BpInvestigateTaskSummary summary = saveTaskSummary(each.getTenentId(), each.getOrgCode(), DateUtil.beginOfDay(DateUtil.date()), each.getId());
                    todayGenUserSummaryList.add(summary);
                }
            });
        }
        if (CollectionUtil.isNotEmpty(todayGenUserSummaryList)) {
            bpInvestigateTaskSummaryService.saveBatch(todayGenUserSummaryList);
            todayContainsSummary.addAll(todayGenUserSummaryList);
        }
        return todayContainsSummary;
    }

    /**
     * 定时check排查任务状态
     */
    @Scheduled(cron = "${investigate.task.check.schedule}")
    public void taskCheck() {
        log.info("investigate schedule check task status begin...");
        Date currDate = new Date();
        List<BpInvestigateTask> list = taskDao.findByCheckDateLessThanAndStatusAndSourceType(new Date(), InvestigateTaskStatus.NEED.getStatus(), 0);
        if (CollectionUtils.isEmpty(list)) {
            log.info("non time out task find...");
            return;
        }
        list.forEach(r -> {
            // 每X天的任务可做周期调整为X天
            // 目前第定时任务为每天第05秒执行，截止时间为 开始时间 + X天 的第一秒，考虑到定时任务可能调整到第0秒，故使用 开始时间 + (X-1)天 的最后一秒
            if ((CycleType.D.getValue().equals(r.getPeriodUnit())) && r.getPeriodDays() != null) {
                Date checkDate = DateUtil.beginOfDay(r.getCheckDate());
                DateTime deadLine = DateUtil.endOfDay(DateUtil.offsetDay(checkDate, r.getPeriodDays() - 1));
                if (currDate.after(deadLine)) {
                    r.setStatus(InvestigateTaskStatus.NO.getStatus());
                }
            } else if (CycleType.MONTHS.getValue().equals(r.getPeriodUnit()) && r.getPeriodDays() != null) {
                Date checkDate = DateUtil.beginOfDay(r.getCheckDate());
                DateTime deadLine = DateUtil.endOfMonth(DateUtil.offsetMonth(checkDate, r.getPeriodDays() - 1));
                if (currDate.after(deadLine)) {
                    r.setStatus(InvestigateTaskStatus.NO.getStatus());
                }
            }else if (Objects.isNull(r.getPeriodUnit()) && Objects.isNull(r.getPeriodDays())) {  // 临时性任务，没有周期单位和周期值
                if (currDate.after(r.getDeadline())) {
                    r.setStatus(InvestigateTaskStatus.NO.getStatus());
                }
            } else {
                // 其余周期的任务均为checkDate当天可做
                r.setStatus(InvestigateTaskStatus.NO.getStatus());
            }
        });
        taskDao.saveAll(list);
        List<Long> taskIdList = list.stream().map(r -> r.getId()).collect(Collectors.toList());
        // 同步更新summary
        if (CollectionUtil.isNotEmpty(taskIdList)){
            summaryMapper.updateSummaryStatusBatch(taskIdList, InvestigateTaskStatus.NO.getStatus());
        }
        log.info("investigate schedule check task status end, taskIds:" + taskIdList);
    }

    @Override
    public void taskTimeOutCheckByHand() {
        taskTimeOutCheck();
    }

    /**
     * 整点check排查任务状态，超期未检的生成状态为未检的记录（record+recordItem）
     * 0点由生成任务的Scheduled执行
     */
    @Scheduled(cron = "3 0 1-23 * * ?")
//    @Scheduled(initialDelay = 15000, fixedDelay = 10 * 1000)
    public synchronized void taskTimeOutCheckBySchedule() {
        taskTimeOutCheck();
    }

    private void taskTimeOutCheck() {
        log.info("investigate schedule check task time out status begin...");
        Date currDate = new Date();
        BaseConfig config = configService.findFirstConfig(BaseConfigCodes.PATROL_SUCCESS_CONFIG);
        boolean dontNeedCheck = config != null && Constant.COMMON_SWITCH_ON.equals(config.getValue());

        //未排查通知开关
        boolean noCheck = false;
//        RiskEvaluationControl control = redisClient.hget(builderRiskControlKey(), BaseConfigCodes.RISK_EVALUATION_CONTROL_HASH_KEY, RiskEvaluationControl.class);
        RiskEvaluationControl control = riskEvaluationTempleService.getRiskEvControlFromRedis();
        noCheck = control != null && control.getSwitchState()!= null && Constant.COMMON_SWITCH_ON.equals(control.getSwitchState().toString());
        // 正常可做状态，超时未做的任务
        dealTimeOutTask(currDate, dontNeedCheck,noCheck);
        // 计划处于隐患状态，且已开始、未完成的任务自动完成（正常）
        dealClosedTask(currDate, false);
        log.info("investigate schedule check task time out status end...");
    }

    private String builderRiskControlKey() {
        Long tenantId = Context.getCurrentTenantId();
        String orgCode = Context.getCurrentOrgCode();
        return String.format(BaseConfigCodes.RISK_EVALUATION_CONTROL_REDIS_KEY, tenantId, orgCode);
    }

    /**
     * 更新汇总信息（每1分钟）
     */
    @Scheduled(fixedDelay = 1000 * 60)
    public void updateSummary() {
        RMap<String, String> rMap = redisClient.getMap(Constant.INVESTIGATION_SUMMARY_UPDATE_KEY);
        if (CollectionUtil.isNotEmpty(rMap)) {
            rMap.entrySet().forEach(entry -> {
                String taskId = entry.getKey();
                SummaryToUpDateBean summaryUpdateBean = JSON.parseObject(entry.getValue(), SummaryToUpDateBean.class);
                try {
                    investigateRecordService.updateSummaryTask(summaryUpdateBean.getTime(), Long.valueOf(taskId), summaryUpdateBean.getAction(), summaryUpdateBean.getUserId());
                } catch (Exception e) {
                    log.error("update summary fail,task id: " + taskId, e);
                } finally {
                    redisClient.removeMapKey(Constant.INVESTIGATION_SUMMARY_UPDATE_KEY, taskId);
                }
            });
        }
    }

    /**
     * 处理超时未检的任务
     *
     * @param currDate
     */
    private void dealTimeOutTask(Date currDate, boolean dontNeedCheck,boolean noCheck) {
        // 截止时间已过，未完成
        List<SimpleInvTaskBean> tasks = taskService.findDeadTask(currDate);
        if (CollectionUtil.isEmpty(tasks)) {
            log.info("no time out task found...");
            return;
        }
        List<BpInvestigateRecord> recordList = new ArrayList<>();
        List<BpInvestigateRecordItem> recordItemList = new ArrayList<>();
        Map<Long, BpInvestigatePlan> taskPlanMapping = getTaskPlanMapping(tasks);
        // 等待触发报警和通知提醒列表
        List<TaskTimeOutWaitingToDo> waitingNotifyBeans = new ArrayList<>();

        tasks.forEach(task -> {
            try {
                BpInvestigatePlan plan = taskPlanMapping.get(task.getPlanId());
                BpInvestigateRecord record = genRecord4DeadTask(task, dontNeedCheck);
                recordList.add(record);
                // 未检：异常整改，任务不可做/超时未做
                if (plan != null) {
                    //超期未检异步生成报警和通知提醒
                    waitingNotifyBeans.add(new TaskTimeOutWaitingToDo(plan, task.getId(), AlarmContentTypes.EXPIRED_INVESTIGATE.getCode(), task.getTenentId(), task.getOrgCode(),task.getUserId()));
                }
                // 00:00完成需算作昨日完成，完成时间减去1秒
                SummaryToUpDateBean summaryWillUpdate = new SummaryToUpDateBean(new Date(record.getCheckFinishDate().getTime() - 1), dontNeedCheck ? RecordActionType.NM.getValue() : RecordActionType.UC.getValue(), null);
                redisClient.hput(Constant.INVESTIGATION_SUMMARY_UPDATE_KEY, task.getId().toString(), JSON.toJSONString(summaryWillUpdate));
            } catch (Exception e) {
                log.error("deal time out task error ,task id: {} {}", task.getId(), e);
            }
        });
        List<Long> taskIds = tasks.stream().map(SimpleInvTaskBean::getId).collect(Collectors.toList());
        taskService.batchDown(taskIds, InvestigateTaskStatus.DONE.getStatus());
        // 保存record后返回带主键的实体
        List<BpInvestigateRecord> recordListWithPK = investigateRecordRepository.saveAll(recordList);
        Map<Long, Long> recordPlanMapping = recordListWithPK.stream().collect(Collectors.toMap(BpInvestigateRecord::getId, BpInvestigateRecord::getCheckPlanId));
        recordListWithPK.forEach(record -> {
            recordItemList.add(genRecordItem4DeadTask(record, dontNeedCheck));
        });
        // 保存recordItem
        List<BpInvestigateRecordItem> recordItems = investigateRecordItemDao.saveAll(recordItemList);
        // 未检影响风险点风险等级
        if(noCheck) {
            taskService.affectRiskLever(recordItems, recordPlanMapping);
        }
        // 报警和通知提醒（异步）
        taskService.sendMassageAndAlarm(waitingNotifyBeans);
        log.info("investigate schedule check task time out status end, taskIds:" + taskIds);
    }

    /**
     * 处理已开始，但不可做（计划处于隐患状态的）的任务
     *
     * @param currDate
     */
    private void dealClosedTask(Date currDate, boolean dontNeedCheck) {
        List<SimpleInvTaskBean> closedTasks = taskService.findCloseTask(currDate);
        if (CollectionUtil.isEmpty(closedTasks)) {
            log.info("no close task found...");
            return;
        }
        List<BpInvestigateRecord> recordList = new ArrayList<>();
        List<BpInvestigateRecordItem> recordItemList = new ArrayList<>();
        closedTasks.forEach(task -> {
            try {
                recordList.add(genRecord4ClosedTask(task, currDate, dontNeedCheck));
                SummaryToUpDateBean summaryWillUpdate = new SummaryToUpDateBean(new Date(), RecordActionType.NM.getValue(), null);
                redisClient.hput(Constant.INVESTIGATION_SUMMARY_UPDATE_KEY, task.getId().toString(), JSON.toJSONString(summaryWillUpdate));
            } catch (Exception e) {
                log.error("deal closed task error, task id:{} {} ", task.getId(), e);
            }
        });
        List<Long> taskIds = closedTasks.stream().map(SimpleInvTaskBean::getId).collect(Collectors.toList());
        taskService.batchDown(taskIds, InvestigateTaskStatus.DONE.getStatus());
        // 保存record后返回带主键的实体
        List<BpInvestigateRecord> recordListWithPK = investigateRecordRepository.saveAll(recordList);
        recordListWithPK.forEach(record -> {
            recordItemList.add(genRecordItem4ClosedTask(record));
        });
        // 保存recordItem
        investigateRecordItemDao.saveAll(recordItemList);
        log.info("investigate schedule check close task end, taskIds:" + taskIds);
    }

    private BpInvestigateRecord genRecord4ClosedTask(SimpleInvTaskBean task, Date currDate, boolean dontNeedCheck) {
        BpInvestigateRecord record = new BpInvestigateRecord();
        record.setId(YitIdHelper.nextId());
        record.setCheckPlanId(task.getPlanId())
                .setCheckUserId(-1L)
                .setTaskId(task.getId())
                .setCheckFinishDate(currDate)
                .setTenentId(task.getTenentId())
                .setOrgCode(task.getOrgCode())
                .setCheckResult(RecordActionType.NM.getValue())
                .setUncheckReason(UnCheckReason.UR.getValue())
                .setSourceType(SourceType.NEW.getValue())
                .setCreateBy(-1L)
                .setVerifyState(VerifyState.UNVERIFY.getState())
                .setUncheckReason(UnCheckReason.UR.getValue());
//        if (!dontNeedCheck) {
//            record.setUncheckReason(UnCheckReason.UR.getValue());
//        }


        return record;
    }

    private BpInvestigateRecordItem genRecordItem4ClosedTask(BpInvestigateRecord record) {
        BpInvestigateRecordItem item = new BpInvestigateRecordItem();
        item.setId(YitIdHelper.nextId());
        item.setAction(RecordActionType.NM.getValue());
        item.setRecordId(record.getId());
        item.setCreateBy(-1L);
        item.setTenentId(record.getTenentId());
        item.setOrgCode(record.getOrgCode());
        return item;
    }

    private BpInvestigateRecord genRecord4DeadTask(SimpleInvTaskBean task, boolean dontNeedCheck) {
        return new BpInvestigateRecord().setId(YitIdHelper.nextId()).setCheckPlanId(task.getPlanId())
                .setCheckUserId(-1L)
                .setTaskId(task.getId())
                .setCheckFinishDate(task.getDeadline())
                .setTenentId(task.getTenentId())
                .setOrgCode(task.getOrgCode())
                .setCheckResult(dontNeedCheck ? RecordActionType.NM.getValue() : RecordActionType.UC.getValue())
                .setSourceType(SourceType.NEW.getValue())
                .setCreateBy(-1L)
                .setVerifyState(dontNeedCheck ? VerifyState.NORMAL.getState() : VerifyState.UNVERIFY.getState())
                .setUncheckReason(dontNeedCheck ? UnCheckReason.UR.getValue() : UnCheckReason.TO.getValue());
    }

    private BpInvestigateRecordItem genRecordItem4DeadTask(BpInvestigateRecord record, boolean dontNeedCheck) {
        return new BpInvestigateRecordItem().setId(YitIdHelper.nextId())
                .setAction(dontNeedCheck ? RecordActionType.NM.getValue() : RecordActionType.UC.getValue())
                .setRecordId(record.getId())
                .setCreateBy(-1L)
                .setTenentId(record.getTenentId())
                .setOrgCode(record.getOrgCode());
    }

    /**
     * 获取任务相关的计划的id-entity映射
     *
     * @param tasks
     * @return
     */
    private Map<Long, BpInvestigatePlan> getTaskPlanMapping(List<SimpleInvTaskBean> tasks) {

        Set<Long> planId = tasks.stream().map(SimpleInvTaskBean::getPlanId).collect(Collectors.toSet());

        List<BpInvestigatePlan> plans = planRepository.findAllById(planId);

        if (CollectionUtil.isNotEmpty(plans)) {
            return plans.stream().collect(Collectors.toMap(BpInvestigatePlan::getId, p -> p));
        }
        return new HashMap<>();
    }

    /**
     * 未检生成通知，通知人为排查人员所在部门的部门负责人
     *
     * @param plan
     * @param needPush 是否需要触发个推
     */
    public void addUncheckAppNotify(BpInvestigatePlan plan, Long taskId, boolean needPush,boolean noCheck) {
        Long pointId = plan.getRiskPointId();
        RiskPoint riskPoint = riskPointService.findByIdIgnoreDeleteStatus(pointId);
        String pointName = riskPoint.getName();
        StringBuilder msg = new StringBuilder();
        Set<Long> notifyUser = null;
        if (ExecutiveUnitTypes.USER.getCode().equals(plan.getExecutiveUnit())) {
            List<BaseUser> userList = baseUserDao.findAllById(JSONArray.parseArray(plan.getExecutor(), Long.class));
            String userNamesStr = userList.stream().map(BaseUser::getRealName).collect(Collectors.joining(Constant.SPLIT_COMMA));
            List<Long> userIds = userList.stream().map(BaseUser::getId).collect(Collectors.toList());
            msg.append(Constant.COLON_ENGLISH).append(pointName).append(Constant.COLON_ENGLISH).append(userNamesStr)
                    .append(Constant.EN_LEFT_BRACKETS).append(plan.getCheckRateDescribe()).append(Constant.EN_RIGHT_BRACKETS)
                    .append(AlarmContentTypes.EXPIRED_INVESTIGATE.getValue());
            notifyUser = baseUserDao.getDepartResponsibleByUserIds(userIds);
        } else if (ExecutiveUnitTypes.JOB.getCode().equals(plan.getExecutiveUnit())) {
            String jobName = bpJobDao.findNameById(plan.getUnitId());
            msg.append(Constant.COLON_ENGLISH).append(pointName).append(Constant.COLON_ENGLISH).append(jobName)
                    .append(Constant.EN_LEFT_BRACKETS).append(plan.getCheckRateDescribe()).append(Constant.EN_RIGHT_BRACKETS)
                    .append("岗位排查").append(AlarmContentTypes.EXPIRED_INVESTIGATE.getValue());
            notifyUser = baseUserDao.getDepartResponsibleByUserIds(baseUserDao.findAllUserByJob(plan.getUnitId()));
        }
        if (!CollectionUtils.isEmpty(notifyUser)) {
            if (noCheck){
                ArrayList<CommonBusinessNotice> commonBusinessNotices = new ArrayList<>();
                for (Long userId : notifyUser) {
                    CommonBusinessNotice appNotify = new CommonBusinessNotice();
                    appNotify.setBusinessType(AppBusinessNoticeTypes.INVESTIGATE.getType()).setNoticeContent(msg.toString())
                            .setNoticeUser(userId).setNoticeTitle(AppNotifyContentPre.TASK_TIME_OUT_NOTIFY).setSendTime(new Date())
                            .setRefId(taskId);
                    appNotify.setOrgCode(plan.getOrgCode());
                    appNotify.setTenentId(plan.getTenentId());
                    commonBusinessNotices.add(appNotify);
                }
                // app消息中心
                commonBusinessNoticeService.saveBatch(commonBusinessNotices);
            }
            // 个推
            if (needPush && noCheck) {
                noticeWaitingTriggerListService.save(notifyUser, AppBusinessNoticeTypes.INVESTIGATE.getType(),
                        msg.toString(), BusinessTypeEnum.INVESTIGATE_TASK_UC.getBusinessCode(), taskId.toString(), new Date());
            }
        }
    }

    @Override
    public List<BpInvestigatePlan> findByIds(List<Long> ids) {
        List<BpInvestigatePlan> list = planRepository.findByIds(ids);

        return list;
    }

    @Override
    public List<BpInvestigatePlanBean> findBeanByIds(List<Long> ids) {
        List<BpInvestigatePlanBean> beanList=new ArrayList<>();
        List<BpInvestigatePlan> list = planRepository.findByIds(ids);
        for (BpInvestigatePlan plan : list) {
            BpInvestigatePlanBean bean = new BpInvestigatePlanBean();
            BeanUtils.copyProperties(plan,bean);
            beanList.add(bean);
        }
        return beanList;
    }

    @Override
    public void regenTask(List<Long> planIds) throws ApiBusinessException {
        log.info("investigate schedule produce task begin...");
        // 旧任务，风险点、评估、隐患被删除的，周期设置为0的，禁用的，排除掉，不生成
        List<BpInvestigatePlan> toProducePlanList = planRepository.findNewPlanToProduceById(planIds);
        if (CollectionUtil.isEmpty(toProducePlanList)) {
            return;
        }
        Set<Long> allExecutor = new HashSet<>();
        // 准备生成汇总和任务需要的数据
        Map<Long, BpInvestigatePlan> planMap = toProducePlanList.stream().collect(Collectors.toMap(BpInvestigatePlan::getId, r -> r));
        Map<Long, List<Long>> planExecutor = preparePlanExecutor(toProducePlanList, allExecutor);
        // 任务执行人为空, 则不生成任务
        if (CollectionUtil.isEmpty(allExecutor)) {
            throw new ApiBusinessException(Status.NO_PERSON_CHECK);
        }
        // 风险点，id-name映射
        Set<Long> riskPointSet = toProducePlanList.stream().map(BpInvestigatePlan::getRiskPointId).collect(Collectors.toSet());
        List<RiskPoint> riskPoints = riskPointService.findByIdInIgnoreDeleteStatus(riskPointSet);
        Map<Long, String> pointIdNameMapping = riskPoints.stream().collect(Collectors.toMap(RiskPoint::getId, RiskPoint::getName));
        // 计划风险点-风险区域映射
        Map<Long, RiskDistrict> pointDistrictMap = riskPointService.findPointDistrictMap(riskPointSet);
        // 个推配置
        boolean needPush = false;
        BaseConfig config = configService.findFirstConfig(BaseConfigCodes.INVESTIGATE_GETUI_ON_OFF);
        needPush = null != config && Constant.COMMON_SWITCH_ON.equals(config.getValue());
        //即将过期开关
        boolean expireCheck = false;
        String expireStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.NOTICE_ABOUT_TO_EXPIRE);
        expireCheck = org.apache.commons.lang3.StringUtils.isNotBlank(expireStatus) && Constant.COMMON_SWITCH_ON.equals(expireStatus);
        //任务生成通知开关
        boolean generateCheck = false;
        String generateStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.TASK_GENERATION_NOTIFICATION);
        generateCheck = org.apache.commons.lang3.StringUtils.isNotBlank(generateStatus) && Constant.COMMON_SWITCH_ON.equals(generateStatus);
        // 生成汇总
        List<BpInvestigateTaskSummary> todayGenUserSummaryList = generateSummary(allExecutor);
        Map<Long, BpInvestigateTaskSummary> todayGenUserSummaryMap = todayGenUserSummaryList.stream().collect(Collectors.toMap(BpInvestigateTaskSummary::getUserId, e -> e));
        // 此次生成的任务
        final List<BpInvestigateTask> taskList = new ArrayList<>();
        final List<BpInvestigateTaskAssign> taskAssignList = new ArrayList<>();
        // 生成任务个推
        final NotifyListsBean notifys = new NotifyListsBean();

        ArrayList<BpInvestigatePlan> hadProduceplan = new ArrayList<>();
        Map<Long,List<BpInvestigateTask>> taskMap = new HashMap<>();
        for (BpInvestigatePlan plan : toProducePlanList) {
            // 一条不正确的计划，不应影响其他计划的生成
            try {
                List<Long> executor = planExecutor.get(plan.getId());
                if (!CollectionUtils.isEmpty(executor)) {
                    GeneratePlanTuple tuple = generateTaskByPlan2(plan, taskList, pointDistrictMap);
                    if (tuple.getGenerateFlag()) {
                        // 第一次生成，lastProduceDate设置为计划开始时间，针对如七号新增一号开始的计划。(周、日任务无开始时间，时任务直接设置当前时间)
                        if (plan.getLastProduceDate() == null && !CycleType.W.getValue().equals(plan.getPeriodUnit()) && !CycleType.D.getValue().equals(plan.getPeriodUnit()) && !CycleType.H.getValue().equals(plan.getPeriodUnit())) {
                            plan.setLastProduceDate(plan.getPlanStartTime());
                        } else {
                            plan.setLastProduceDate(tuple.getLastProduceDate());
                        }
                        hadProduceplan.add(plan);
                    }
                    //查询今天该计划的任务
                    List<BpInvestigateTask> tasks = taskService.findTodayTask(plan.getId());
                    taskMap.put(plan.getId(),tasks);
                }
            } catch (Exception e) {
                log.error("隐患排查任务生成出错：计划id{}-{}", plan.getId(), e);
            }
        }
        if (CollectionUtil.isNotEmpty(hadProduceplan)) {
            planRepository.saveAll(hadProduceplan);
        }

        if (CollectionUtil.isNotEmpty(taskList)) {
            for (BpInvestigateTask task : taskList){
                List<BpInvestigateTask> tasks = taskMap.get(task.getPlanId());
                //查询是否已有任务
                if (!CollectionUtils.isEmpty(tasks)){
                    List<BpInvestigateTask> oldTasks = tasks.stream().filter(t->t.getCheckDate().equals(task.getCheckDate())).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(oldTasks)){
                        task.setId(oldTasks.get(0).getId());
                        continue;
                    }
                }
                taskService.save(task);
            }
            List<SimpleTaskAssign> assigns = bpInvestigateTaskAssignService.getByTaskIds(taskList.stream().map(BpInvestigateTask::getId).collect(Collectors.toList()));
            Map<Long,List<SimpleTaskAssign>> assignsMap = assigns.stream().collect(Collectors.groupingBy(SimpleTaskAssign::getTaskId));
            //合并
            boolean finalNeedPush = needPush;
            // 准备assign数据，通过排班模块过滤
            List<TaskForAssignBean> assignBeans = taskList.stream().parallel()
                    .map(task -> new TaskForAssignBean(
                                    task.getId(),
                                    task.getCheckDate(),
                                    planExecutor.get(task.getPlanId())
                            )
                    ).collect(Collectors.toList());
            // 排班过滤分配人
            assignBeans = attendanceApiUserService.batchUserTaskCheck(assignBeans);
            Map<Long, List<Long>> taskAssignMap = assignBeans.stream().parallel()
                    .collect(Collectors.toMap(TaskForAssignBean::getTaskId, TaskForAssignBean::getAssignList));
            boolean finalExpireCheck = expireCheck;
            boolean finalGenerateCheck = generateCheck;
            taskList.forEach(task -> {
                BpInvestigatePlan plan = planMap.get(task.getPlanId());
                // assign
                List<Long> assignUserIds = taskAssignMap.get(task.getId());
                List<SimpleTaskAssign> assignList = assignsMap.get(task.getId());
                if (CollectionUtil.isNotEmpty(assignUserIds)) {
                    assignUserIds.forEach(userId -> {
                        //判断是否已生成
                        if (!CollectionUtils.isEmpty(assignList)){
                            List<SimpleTaskAssign> taskAssigns = assignList.stream().filter(a-> Objects.equals(a.getUserId(), userId)).collect(Collectors.toList());
                            if (!CollectionUtils.isEmpty(taskAssigns)){
                                return;
                            }
                        }
                        BpInvestigateTaskAssign assign = new BpInvestigateTaskAssign();
                        BpInvestigateTaskSummary summary = todayGenUserSummaryMap.get(userId);
                        if (null != summary) {
                            summary.setTaskCount(summary.getTaskCount() + 1);
                            // 今日有任务生成的汇总不允许删除
                            summary.setHasProcessRecord(Boolean.TRUE);
                            assign.setSummaryId(summary.getId());
                        }
                        assign.setAssignUser(userId);
                        assign.setTaskId(task.getId());
                        assign.setTenentId(task.getTenentId());
                        assign.setOrgCode(task.getOrgCode());
                        taskAssignList.add(assign);
                    });
                }
                // notify
                generateTaskAppNotifyForNew(assignUserIds, pointIdNameMapping.get(plan.getRiskPointId()), task, notifys, finalNeedPush, finalGenerateCheck);
                addTaskComingDeadNotify(task, assignUserIds, notifys, finalNeedPush,finalExpireCheck);
            });
            // 保存assign
            if (CollectionUtil.isNotEmpty(taskAssignList)) {
                List<List<BpInvestigateTaskAssign>> taskPartition = Lists.partition(taskAssignList,1000);
                for (List<BpInvestigateTaskAssign> list : taskPartition){
                    list.forEach(taskAssign->taskAssign.setId(YitIdHelper.nextId()));
                    bpInvestigateTaskAssignService.saveBatch(list);
                }
            }
            if (CollectionUtil.isNotEmpty(todayGenUserSummaryList)) {
                bpInvestigateTaskSummaryService.saveOrUpdateBatch(todayGenUserSummaryList);
            }
            // 保存通知
            saveNotifys(notifys);
        }
        // 任务生成结束，检查一次，将隐患中的计划生成的任务刷成正常(隐患整改)
        taskTimeOutCheck();
        log.info("investigate schedule produce task end...");
    }

    @Override
    public String regenAllTask() {
        log.info("investigate schedule produce task begin...");
        // 旧任务，风险点、评估、隐患被删除的，周期设置为0的，禁用的，排除掉，不生成
        List<BpInvestigatePlan> toProducePlanList = planRepository.findNewPlanToProduce();
        if (CollectionUtil.isEmpty(toProducePlanList)) {
            return "暂无任务可创建！";
        }
        Set<Long> allExecutor = new HashSet<>();
        // 准备生成汇总和任务需要的数据
        Map<Long, BpInvestigatePlan> planMap = toProducePlanList.stream().collect(Collectors.toMap(BpInvestigatePlan::getId, r -> r));
        Map<Long, List<Long>> planExecutor = preparePlanExecutor(toProducePlanList, allExecutor);
        // 任务执行人为空, 则不生成任务
        if (CollectionUtil.isEmpty(allExecutor)) {
            return "任务无执行人,不创建任务";
        }
        // 风险点，id-name映射
        Set<Long> riskPointSet = toProducePlanList.stream().map(BpInvestigatePlan::getRiskPointId).collect(Collectors.toSet());
        List<RiskPoint> riskPoints = riskPointService.findByIdInIgnoreDeleteStatus(riskPointSet);
        Map<Long, String> pointIdNameMapping = riskPoints.stream().collect(Collectors.toMap(RiskPoint::getId, RiskPoint::getName));
        // 计划风险点-风险区域映射
        Map<Long, RiskDistrict> pointDistrictMap = riskPointService.findPointDistrictMap(riskPointSet);
        // 个推配置
        boolean needPush = false;
        BaseConfig config = configService.findFirstConfig(BaseConfigCodes.INVESTIGATE_GETUI_ON_OFF);
        needPush = null != config && Constant.COMMON_SWITCH_ON.equals(config.getValue());
        //即将过期开关
        boolean expireCheck = false;
        String expireStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.NOTICE_ABOUT_TO_EXPIRE);
        expireCheck = org.apache.commons.lang3.StringUtils.isNotBlank(expireStatus) && Constant.COMMON_SWITCH_ON.equals(expireStatus);
        //任务生成通知开关
        boolean generateCheck = false;
        String generateStatus = investigateConfigService.getStatusByName(InvestigateConfigNames.TASK_GENERATION_NOTIFICATION);
        generateCheck = org.apache.commons.lang3.StringUtils.isNotBlank(generateStatus) && Constant.COMMON_SWITCH_ON.equals(generateStatus);
        // 生成汇总
        List<BpInvestigateTaskSummary> todayGenUserSummaryList = generateSummary(allExecutor);
        Map<Long, BpInvestigateTaskSummary> todayGenUserSummaryMap = todayGenUserSummaryList.stream().collect(Collectors.toMap(BpInvestigateTaskSummary::getUserId, e -> e));
        // 此次生成的任务
        final List<BpInvestigateTask> taskList = new ArrayList<>();
        final List<BpInvestigateTaskAssign> taskAssignList = new ArrayList<>();
        // 生成任务个推
        final NotifyListsBean notifys = new NotifyListsBean();

        ArrayList<BpInvestigatePlan> hadProduceplan = new ArrayList<>();
        Map<Long,List<BpInvestigateTask>> taskMap = new HashMap<>();
        for (BpInvestigatePlan plan : toProducePlanList) {
            // 一条不正确的计划，不应影响其他计划的生成
            try {
                List<Long> executor = planExecutor.get(plan.getId());
                if (!CollectionUtils.isEmpty(executor)) {
                    GeneratePlanTuple tuple = generateTaskByPlan2(plan, taskList, pointDistrictMap);
                    if (tuple.getGenerateFlag()) {
                        // 第一次生成，lastProduceDate设置为计划开始时间，针对如七号新增一号开始的计划。(周、日任务无开始时间，时任务直接设置当前时间)
                        if (plan.getLastProduceDate() == null && !CycleType.W.getValue().equals(plan.getPeriodUnit()) && !CycleType.D.getValue().equals(plan.getPeriodUnit()) && !CycleType.H.getValue().equals(plan.getPeriodUnit())) {
                            plan.setLastProduceDate(plan.getPlanStartTime());
                        } else {
                            plan.setLastProduceDate(tuple.getLastProduceDate());
                        }
                        hadProduceplan.add(plan);
                    }
                    //查询今天该计划的任务
                    List<BpInvestigateTask> tasks = taskService.findTodayTask(plan.getId());
                    taskMap.put(plan.getId(),tasks);
                }
            } catch (Exception e) {
                log.error("隐患排查任务生成出错：计划id{}-{}", plan.getId(), e);
            }
        }
        if (CollectionUtil.isNotEmpty(hadProduceplan)) {
            planRepository.saveAll(hadProduceplan);
        }

        if (CollectionUtil.isNotEmpty(taskList)) {
            for (BpInvestigateTask task : taskList){
                List<BpInvestigateTask> tasks = taskMap.get(task.getPlanId());
                //查询是否已有任务
                if (!CollectionUtils.isEmpty(tasks)){
                    List<BpInvestigateTask> oldTasks = tasks.stream().filter(t->t.getCheckDate().equals(task.getCheckDate())).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(oldTasks)){
                        task.setId(oldTasks.get(0).getId());
                        continue;
                    }
                }
                taskService.save(task);
            }
            List<SimpleTaskAssign> assigns = bpInvestigateTaskAssignService.getByTaskIds(taskList.stream().map(BpInvestigateTask::getId).collect(Collectors.toList()));
            Map<Long,List<SimpleTaskAssign>> assignsMap = assigns.stream().collect(Collectors.groupingBy(SimpleTaskAssign::getTaskId));
            //合并
            boolean finalNeedPush = needPush;
            // 准备assign数据，通过排班模块过滤
            List<TaskForAssignBean> assignBeans = taskList.stream().parallel()
                    .map(task -> new TaskForAssignBean(
                                    task.getId(),
                                    task.getCheckDate(),
                                    planExecutor.get(task.getPlanId())
                            )
                    ).collect(Collectors.toList());
            // 排班过滤分配人
            assignBeans = attendanceApiUserService.batchUserTaskCheck(assignBeans);
            Map<Long, List<Long>> taskAssignMap = assignBeans.stream().parallel()
                    .collect(Collectors.toMap(TaskForAssignBean::getTaskId, TaskForAssignBean::getAssignList));
            boolean finalExpireCheck = expireCheck;
            boolean finalGenerateCheck = generateCheck;
            taskList.forEach(task -> {
                BpInvestigatePlan plan = planMap.get(task.getPlanId());
                // assign
                List<Long> assignUserIds = taskAssignMap.get(task.getId());
                List<SimpleTaskAssign> assignList = assignsMap.get(task.getId());
                if (CollectionUtil.isNotEmpty(assignUserIds)) {
                    assignUserIds.forEach(userId -> {
                        //判断是否已生成
                        if (!CollectionUtils.isEmpty(assignList)){
                            List<SimpleTaskAssign> taskAssigns = assignList.stream().filter(a-> Objects.equals(a.getUserId(), userId)).collect(Collectors.toList());
                            if (!CollectionUtils.isEmpty(taskAssigns)){
                                return;
                            }
                        }
                        BpInvestigateTaskAssign assign = new BpInvestigateTaskAssign();
                        BpInvestigateTaskSummary summary = todayGenUserSummaryMap.get(userId);
                        if (null != summary) {
                            summary.setTaskCount(summary.getTaskCount() + 1);
                            // 今日有任务生成的汇总不允许删除
                            summary.setHasProcessRecord(Boolean.TRUE);
                            assign.setSummaryId(summary.getId());
                        }
                        assign.setAssignUser(userId);
                        assign.setTaskId(task.getId());
                        assign.setTenentId(task.getTenentId());
                        assign.setOrgCode(task.getOrgCode());
                        taskAssignList.add(assign);
                    });
                }
                // notify
                generateTaskAppNotifyForNew(assignUserIds, pointIdNameMapping.get(plan.getRiskPointId()), task, notifys, finalNeedPush, finalGenerateCheck);
                addTaskComingDeadNotify(task, assignUserIds, notifys, finalNeedPush,finalExpireCheck);
            });
            // 保存assign
            if (CollectionUtil.isNotEmpty(taskAssignList)) {
                List<List<BpInvestigateTaskAssign>> taskPartition = Lists.partition(taskAssignList,1000);
                for (List<BpInvestigateTaskAssign> list : taskPartition){
                    list.forEach(taskAssign->taskAssign.setId(YitIdHelper.nextId()));
                    bpInvestigateTaskAssignService.saveBatch(list);
                }
            }
            if (CollectionUtil.isNotEmpty(todayGenUserSummaryList)) {
                bpInvestigateTaskSummaryService.saveOrUpdateBatch(todayGenUserSummaryList);
            }
            // 保存通知
            saveNotifys(notifys);
        }
        // 任务生成结束，检查一次，将隐患中的计划生成的任务刷成正常(隐患整改)
        taskTimeOutCheck();
        log.info("investigate schedule produce task end...");
        return "创建任务成功"+taskAssignList.size()+"条";
    }

    @Override
    public void deleteTaskByPlanId(List<Long> planIds) {
        TaskDelDTO bean = new TaskDelDTO();
        bean.setIds(planIds);
        bean.setDelAllFlag(true);
        taskService.deleteBatch(bean);
    }

    @Override
    public List<Long> getPlanId(List<Long> contentIds) {
        List<BpInvestigatePlan> plans = planRepository.findByHiddenDangerContentIds(contentIds);
        List<Long> planIds = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(plans)){
            planIds = plans.stream().map(BpInvestigatePlan::getId).collect(Collectors.toList());
        }
        return planIds;
    }

    @Override
    public List<Long> getPlanIdByContentId(List<Long> contentIds) {
        return planRepository.findIdsByHiddenDangerContentIds(contentIds);
    }

    private GeneratePlanTuple generateTaskByPlan2(BpInvestigatePlan plan, List<BpInvestigateTask> taskList, Map<Long, RiskDistrict> pointDistrictMap) {
        final Integer period = plan.getPeriodDays();
        final String periodUnit = plan.getPeriodUnit();
        final String periodJson = plan.getPeriodJson();
        final Date planStartTime = plan.getPlanStartTime();
        final Date planEndTime = plan.getPlanEndTime();
        final Date lastProduceDate = null;

        final CycleType cycleType = CycleType.find(periodUnit);
        final Date curDate = DateUtil.beginOfDay(DateUtil.date());
        Date taskDeadline = null;
        boolean generateFlag = false;
        final Date curEndDate = DateUtil.endOfDay(DateUtil.date());
        if (CycleType.H.getValue().equals(periodUnit) || ((planStartTime == null || planStartTime.before(curEndDate)) && (planEndTime == null || planEndTime.after(curDate)))) {
            Date nextGenerateTime = null;
            switch (cycleType) {
                case Y:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period * 12);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(planStartTime, period * 12));
                    } else {
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(nextGenerateTime, period * 12));
                    }
                    break;
                case M:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(planStartTime, period));
                        // 无上次生成时间，且taskDeadline早于今日，表示有抹掉过lastProduceDate，需要找到大于当前时间的第一个截止时间
                        if (period > 0) { // 逻辑上不会出现period小于等于0的情况，以防万一出现死循环
                            while (taskDeadline.before(curDate) || taskDeadline.equals(curDate)) {
                                taskDeadline = DateUtil.offsetMonth(taskDeadline, period);
                            }
                        }
                    } else {
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(nextGenerateTime, period));
                    }
                    break;
                case D:
                    // 为空时，第一次生成，直接取curDate，
                    // 不为空时，非第一次生成，lastProduceDate与curDate相差天数大于周期数，表示中间宕机过，导致未生成任务，未更新到lastProduceDate
                    // 不为空且lastProduceDate与curDate相差天数不大于周期数，表示正常生成，nextGenerateTime为lastProduceDate加上周期
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.between(lastProduceDate, curEndDate, DateUnit.DAY) > period ? curDate : DateUtil.offsetDay(lastProduceDate, period);
                    taskDeadline = DateUtil.endOfDay(DateUtil.offsetDay(nextGenerateTime, period - 1));
                    break;
                case MONTHS:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.endOfDay(DateUtil.offsetMonth(planStartTime, period - 1));
                    } else {
                        Date date = DateUtil.offsetMonth(nextGenerateTime, period - 1);
                        taskDeadline = DateUtil.endOfMonth(DateUtil.offsetMonth(nextGenerateTime, period - 1));
                    }
                    break;
            }
            if (nextGenerateTime != null) {
                if (curDate.after(nextGenerateTime) || curDate.equals(nextGenerateTime)) {
                    generateFlag = saveBpInvestigateTask(plan, curDate, taskDeadline, taskList, pointDistrictMap);
                }
            }
            switch (cycleType) {
                case W:
                    List<Integer> weeks = JSON.parseArray(periodJson, Integer.class);
                    int dayOfWeek = LocalDate.now().getDayOfWeek().getValue();
                    if (weeks.contains(dayOfWeek)) {
                        if (lastProduceDate == null) {
                            generateFlag = saveBpInvestigateTask(plan, curDate, DateUtil.endOfDay(curDate), taskList, pointDistrictMap);
                        } else {
                            if (weeks.contains(dayOfWeek) && (curDate.after(lastProduceDate))) {
                                generateFlag = saveBpInvestigateTask(plan, curDate, DateUtil.endOfDay(curDate), taskList, pointDistrictMap);
                            }
                        }
                    }
                    break;
                case H:
                    if (lastProduceDate != null) {
                        nextGenerateTime = DateUtil.offsetDay(lastProduceDate, 1);
                        if (curDate.after(nextGenerateTime) || curDate.equals(nextGenerateTime)) {
                            generateFlag = generateHourTask(plan, curDate, taskList, pointDistrictMap);
                        }
                    } else {
                        generateFlag = generateHourTask(plan, curDate, taskList, pointDistrictMap);
                    }
                    break;
            }
        }
        return GeneratePlanTuple.builder().generateFlag(generateFlag).lastProduceDate(curDate).build();
    }


    private BpInvestigateTask newTask(BpInvestigatePlan plan, Long userId, Date checkDate) {
        BpInvestigateTask task = new BpInvestigateTask();
        task.setId(YitIdHelper.nextId());
        task.setPlanId(plan.getId());
        task.setCheckDate(checkDate);
        try {
            Date date = DateUtil.beginOfDay(new Date());
            task.setCheckDate(date);
            task.setCheckEndDate(DateUtil.endOfDay(date));
            task.setDeadline(DateUtil.endOfDay(date));
        } catch (Exception e) {
            task.setCheckDate(DateUtil.beginOfDay(new Date()));
            task.setDeadline(DateUtil.beginOfDay(new Date()));
        }
        task.setUserId(userId);
        task.setStatus("待完成");
        task.setTenentId(plan.getTenentId());
        task.setOrgCode(plan.getOrgCode());
        task.setSourceType(SourceType.OLD.getValue());
        task.setCheckRateDescribe(plan.getCheckRateDescribe());
        task.setPeriodDays(plan.getPeriodDays());
        task.setPeriodUnit(plan.getPeriodUnit());

        if ("临时性".equals(plan.getCheckRate())) {
            if (plan.getPlanEndDate() != null) {
                ZoneId zone = ZoneId.systemDefault();
                Instant instant = plan.getPlanEndDate().atStartOfDay().atZone(zone).toInstant();
                java.util.Date date = Date.from(instant);
                task.setCheckEndDate(DateUtil.endOfDay(date));
                task.setDeadline(DateUtil.endOfDay(date));
            }
        } else {
            if ("日".equals(plan.getPeriodUnit())) {
                // 每几日
                if (plan.getPeriodDays() != null && plan.getPeriodDays() > 1) {
                    DateTime dateTime = DateUtil.beginOfDay(task.getCheckDate());
                    Integer periodDays = plan.getPeriodDays();
                    if (periodDays == null) {
                        periodDays = 1;
                    }
                    if (plan.getLastProduceDate() != null) {
                        Date lastProduceDate = plan.getLastProduceDate();
                        dateTime = DateUtil.beginOfDay(lastProduceDate);
                    }
                    DateTime dateTime2 = DateUtil.offsetDay(dateTime, periodDays);
                    task.setCheckEndDate(dateTime2);
                    task.setDeadline(dateTime2);
                } else { // 日 / 周
                    DateTime dateTime = DateUtil.beginOfDay(task.getCheckDate());
                    Integer periodDays = plan.getPeriodDays();
                    if (periodDays == null) {
                        periodDays = 1;
                    }
                    task.setCheckEndDate(dateTime);
                    task.setDeadline(DateUtil.endOfDay(DateUtil.offsetDay(dateTime, periodDays - 1)));
                }
            }
            if ("每几月".equals(plan.getPeriodUnit())) {
                setMonthUnitDate(plan, task);
            }
            if ("月".equals(plan.getPeriodUnit())) {
                DateTime dateTime = DateUtil.beginOfDay(task.getCheckDate());
                task.setCheckEndDate(DateUtil.endOfDay(dateTime));
                task.setDeadline(DateUtil.endOfDay(dateTime));
            }
            if ("周".equals(plan.getPeriodUnit())) {
                DateTime dateTime = DateUtil.beginOfDay(task.getCheckDate());
                task.setCheckEndDate(DateUtil.endOfDay(dateTime));
                task.setDeadline(DateUtil.endOfDay(dateTime));
            }
        }
        return task;
    }

    private void setMonthUnitDate(BpInvestigatePlan plan, BpInvestigateTask task) {
        Integer monthStartDate = plan.getMonthStartDate();
        final Integer period = plan.getPeriodDays();

        int start = monthStartDate;

        if (period == 0) {
            task.setCheckDate(DateTimeUtil.localDate2Date(LocalDate.now()));
        }
        while (start <= 12) {
            LocalDate now = LocalDate.now();
            LocalDate localDate = now.withMonth(start);
            LocalDate local = localDate.with(TemporalAdjusters.firstDayOfMonth());
            Date date = DateTimeUtil.localDate2Date(local);

            Date taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(date, period));
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis >= date.getTime() && currentTimeMillis < taskDeadline.getTime()) {
                task.setCheckDate(date);
                task.setCheckEndDate(DateUtil.endOfDay(new Date(taskDeadline.getTime() - 1000L)));
                task.setDeadline(task.getCheckEndDate());
                return;
            }

            start += period;
        }

        task.setCheckDate(DateTimeUtil.localDate2Date(LocalDate.now()));
        task.setCheckEndDate(DateUtil.endOfDay(task.getCheckDate()));
        task.setDeadline(task.getCheckEndDate());
    }

    /**
     * @param task
     */
    private void newTaskAssign(BpInvestigateTask task) {
        BpInvestigateTaskAssign assign = new BpInvestigateTaskAssign();
        assign.setId(YitIdHelper.nextId());
        assign.setTaskId(task.getId()).setAssignUser(task.getUserId());
        assign.setTenentId(task.getTenentId());
        assign.setOrgCode(task.getOrgCode());
        bpInvestigateTaskAssignService.save(assign);
    }

    private GeneratePlanTuple generateTaskByPlan(BpInvestigatePlan plan, List<BpInvestigateTask> taskList, Map<Long, RiskDistrict> pointDistrictMap) {
        final Integer period = plan.getPeriodDays();
        final String periodUnit = plan.getPeriodUnit();
        final String periodJson = plan.getPeriodJson();
        final Date planStartTime = plan.getPlanStartTime();
        final Date planEndTime = plan.getPlanEndTime();
        final Date lastProduceDate = plan.getLastProduceDate();

        final CycleType cycleType = CycleType.find(periodUnit);
        final Date curDate = DateUtil.beginOfDay(DateUtil.date());
        Date taskDeadline = null;
        boolean generateFlag = false;
        final Date curEndDate = DateUtil.endOfDay(DateUtil.date());
        if (CycleType.H.getValue().equals(periodUnit) || ((planStartTime == null || planStartTime.before(curEndDate)) && (planEndTime == null || planEndTime.after(curDate)))) {
            Date nextGenerateTime = null;
            switch (cycleType) {
                case Y:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period * 12);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(planStartTime, period * 12));
                    } else {
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(nextGenerateTime, period * 12));
                    }
                    break;
                case M:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(planStartTime, period));
                        //如果截止时间小于当前时间
                        if (taskDeadline.before(DateUtil.date())) {
                            taskDeadline=DateUtil.beginOfDay(DateUtil.offsetMonth(planStartTime, period*2));
                        }
                        // 无上次生成时间，且taskDeadline早于今日，表示有抹掉过lastProduceDate，需要找到大于当前时间的第一个截止时间
                        if (period > 0) { // 逻辑上不会出现period小于等于0的情况，以防万一出现死循环
                            while (taskDeadline.before(curDate) || taskDeadline.equals(curDate)) {
                                taskDeadline = DateUtil.offsetMonth(taskDeadline, period);
                            }
                        }
                    } else {
                        taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(nextGenerateTime, period));
                        // 如果截止时间小于等于当前时间, 做兜底处理
                        if (period > 0) {
                            while (taskDeadline.before(curDate) || taskDeadline.equals(curDate)) {
                                taskDeadline = DateUtil.beginOfDay(DateUtil.offsetMonth(taskDeadline, period));
                            }
                        }
                    }
                    break;
                case D:
                    // 为空时，第一次生成，直接取curDate，
                    // 不为空时，非第一次生成，lastProduceDate与curDate相差天数大于周期数，表示中间宕机过，导致未生成任务，未更新到lastProduceDate
                    // 不为空且lastProduceDate与curDate相差天数不大于周期数，表示正常生成，nextGenerateTime为lastProduceDate加上周期
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.between(lastProduceDate, curEndDate, DateUnit.DAY) > period ? curDate : DateUtil.offsetDay(lastProduceDate, period);
                    taskDeadline = DateUtil.endOfDay(DateUtil.offsetDay(nextGenerateTime, period - 1));
                    break;
                case MONTHS:
                    nextGenerateTime = lastProduceDate == null ?
                            curDate : DateUtil.offsetMonth(lastProduceDate, period);
                    if (lastProduceDate == null) {
                        // 第一次生成，taskDeadline为curDate加上周期
                        taskDeadline = DateUtil.endOfDay(DateUtil.offsetMonth(planStartTime, period - 1));
                    } else {
                        Date date = DateUtil.offsetMonth(nextGenerateTime, period - 1);
                        taskDeadline = DateUtil.endOfMonth(DateUtil.offsetMonth(nextGenerateTime, period - 1));
                    }
                    break;
            }
            if (nextGenerateTime != null) {
                if (curDate.after(nextGenerateTime) || curDate.equals(nextGenerateTime)) {
                    generateFlag = saveBpInvestigateTask(plan, curDate, taskDeadline, taskList, pointDistrictMap);
                }
            }
            switch (cycleType) {
                case W:
                    List<Integer> weeks = JSON.parseArray(periodJson, Integer.class);
                    int dayOfWeek = LocalDate.now().getDayOfWeek().getValue();
                    if (weeks.contains(dayOfWeek)) {
                        if (lastProduceDate == null) {
                            generateFlag = saveBpInvestigateTask(plan, curDate, DateUtil.endOfDay(curDate), taskList, pointDistrictMap);
                        } else {
                            if (weeks.contains(dayOfWeek) && (curDate.after(lastProduceDate))) {
                                generateFlag = saveBpInvestigateTask(plan, curDate, DateUtil.endOfDay(curDate), taskList, pointDistrictMap);
                            }
                        }
                    }
                    break;
                case H:
                    if (lastProduceDate != null) {
                        nextGenerateTime = DateUtil.offsetDay(lastProduceDate, 1);
                        if (curDate.after(nextGenerateTime) || curDate.equals(nextGenerateTime)) {
                            generateFlag = generateHourTask(plan, curDate, taskList, pointDistrictMap);
                        }
                    } else {
                        generateFlag = generateHourTask(plan, curDate, taskList, pointDistrictMap);
                    }
                    break;
            }
        }
        return GeneratePlanTuple.builder().generateFlag(generateFlag).lastProduceDate(curDate).build();
    }

    public static void main(String[] args) {
//        System.out.println(DateUtil.endOfDay(DateUtil.offsetMonth(DateUtil.date(), 1 * 12)));
        System.out.println(checkInMonthPeriod(new Date(), 3, 1));
        System.out.println(checkInMonthPeriod(new Date(), 4, 1));
        System.out.println(checkInMonthPeriod(new Date(), 1, 1));
        System.out.println(checkInMonthPeriod(new Date(), 4, 5));
        System.out.println(getPeriodStartMonth(new Date(), 2, 5) + "");
        System.out.println(getPeriodStartMonth(new Date(), 2, 2) + "");
        System.out.println(getPeriodStartMonth(new Date(), 2, 1) + "");
        System.out.println(getPeriodStartMonth(new Date(), 4, 1) + "");
    }

    private boolean generateHourTask(BpInvestigatePlan plan, final Date curDate, List<BpInvestigateTask> taskList, Map<Long, RiskDistrict> pointDistrictMap) {
        final Date lastProduceDate = plan.getLastProduceDate();
        final Date planStartTime = plan.getPlanStartTime();
        final Date planEndTime = plan.getPlanEndTime();
        final Integer periodDays = plan.getPeriodDays();
        int hour = DateUtil.hour(planStartTime, true);
        int endHour = 24;
        if (planEndTime != null && planEndTime.after(planStartTime)){
            endHour = DateUtil.hour(planEndTime, true)-1;
        }
        //区分开24点
        int times;
        if (endHour == 24){
            times = hour > 0 ? (endHour - hour) / periodDays + 1 : endHour;
        }else {
            times = (endHour - hour) / periodDays + 1;
        }
        Date checkDate = null;
        // 上次生成时间+周期 >= 当前日的最后一刻，不生成
        if (lastProduceDate != null && DateUtil.offsetHour(lastProduceDate, periodDays).isAfterOrEquals(DateUtil.endOfDay(curDate))) {
            return false;
        }
        for (int i = 0; i < times; i++) {
            checkDate = i > 0 ? DateUtil.offsetHour(checkDate, periodDays) : DateUtil.offsetHour(DateUtil.beginOfDay(DateUtil.date()), hour);
            //24点不推送
            if (checkDate.before(DateUtil.endOfDay(curDate))) {
                // taskDeadline更改为当前周期的右边界
                Date taskDeadline = DateUtil.offsetHour(checkDate, periodDays);
                // 时任务已过时的不生成（主要针对手动生成任务）
                if (taskDeadline.after(new Date())) {
                    saveBpInvestigateTask(plan, checkDate, taskDeadline, taskList, pointDistrictMap);
                }
            }
        }
        return times > 0;
    }

    private boolean saveBpInvestigateTask(BpInvestigatePlan plan, final Date checkDate,
                                          final Date taskDeadline, List<BpInvestigateTask> taskList, Map<Long, RiskDistrict> pointDistrictMap) {
        BpInvestigateTask task = new BpInvestigateTask();
        task.setId(YitIdHelper.nextId());
        RiskDistrict district = pointDistrictMap.get(plan.getRiskPointId());
        task.setPlanId(plan.getId()).setCheckDate(checkDate)
                .setStatus(InvestigateTaskStatus.NO.getStatus())
                .setSourceType(SourceType.NEW.getValue())
                .setDeadline(taskDeadline)
                .setPeriodUnit(plan.getPeriodUnit())
                .setPeriodDays(plan.getPeriodDays())
                .setCheckRateDescribe(plan.getCheckRateDescribe())
                // task增加风险区域、区域部门，留待后续扩展
                .setDistrictId(null == district ? null : district.getId())
                .setDepartId(null == district ? null : district.getResponsibleDept())
                // 定时任务执行，orgCod和tenentId来自plan
                .setTenentId(plan.getTenentId())
                .setOrgCode(plan.getOrgCode())
                // 实体未继承UserDataEntity，使用MyBatis批量插入需要手动设置公共属性
                .setDeleteFlag(DeleteFlag.EFFECTIVE.getCode())
                .setCreateBy(Context.getCurrentUserId())
                .setUpdateBy(Context.getCurrentUserId())
                .setCreateTime(LocalDateTime.now())
                .setUpdateTime(LocalDateTime.now());
        taskList.add(task);
        return true;
    }

    /**
     * app通知中心（生成通知）、个推
     *
     * @param executor
     */
    void generateTaskAppNotifyForNew(List<Long> executor, String pointName, BpInvestigateTask task, NotifyListsBean notifyLists, boolean needPush, boolean generateCheck) {
        String checkRateDescribe = task.getCheckRateDescribe();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(checkRateDescribe)) {
            // 部分任务周期描述前缀'每'，去除
            if (checkRateDescribe.startsWith("每")) {
                checkRateDescribe = checkRateDescribe.substring(1);
            }
            // 部分任务周期描述后缀'一次'，去除
            if (checkRateDescribe.endsWith("一次")) {
                checkRateDescribe = checkRateDescribe.substring(0, checkRateDescribe.length() - 2);
            }
        }
        SimpleDateFormat dateFormater = SimpleDateFormatCache.getHm();
        String msg = String.format(NotifyContents.PRODUCE_TASK, checkRateDescribe, pointName, dateFormater.format(task.getCheckDate()));
        if (generateCheck){
            ArrayList<CommonBusinessNotice> notifys = new ArrayList<>();
            for (Long userId : executor) {
                CommonBusinessNotice notify = new CommonBusinessNotice();
                notify.setNoticeUser(userId)
                        .setNoticeContent(msg)
                        .setBusinessType(AppBusinessNoticeTypes.INVESTIGATE.getType())
                        .setNoticeTitle(AppNotifyContentPre.TASK_NOTIFY)
                        .setSendTime(new Date())
                        .setRefId(task.getId());
                notifys.add(notify);
            }
            notifyLists.getCommonBusinessNotifyList().addAll(notifys);
        }
        // app个推，配置开关
        if (needPush && generateCheck) {
            noticeWaitingTriggerListService.saveInRam(new HashSet<>(executor), AppBusinessNoticeTypes.INVESTIGATE.getType(),
                    msg, BusinessTypeEnum.INVESTIGATE_GEN_TASK.getBusinessCode(), task.getId().toString(), new Date(), notifyLists.getGeTuiNotifyList());
        }
        // web主页通知
        if (generateCheck){
            indexNotifyService.generateInvestigateTaskNotifyInRam(executor, AppBusinessNoticeTypes.INVESTIGATE.getType(), msg, task, notifyLists.getCommonIndexNotifyList());
            indexNotifyService.generateInvestigateTaskAssignNotifyInRam(executor, AppBusinessNoticeTypes.INVESTIGATE.getType(), msg, task, notifyLists.getCommonIndexNotifyList());
        }

    }

    void generateTaskAppNotifyForOld(Long userId, String rateDescribe, String checkRate, String planName, BpInvestigateTask task, boolean needPush,boolean generateCheck) {
        CommonBusinessNotice notify = new CommonBusinessNotice();
        SimpleDateFormat dateFormater = SimpleDateFormatCache.getHm();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(rateDescribe)) {
            // 部分任务周期描述前缀'每'，去除
            if (rateDescribe.startsWith("每")) {
                rateDescribe = rateDescribe.substring(1);
            }
            // 部分任务周期描述后缀'一次'，去除
            if (rateDescribe.endsWith("一次")) {
                rateDescribe = rateDescribe.substring(0, rateDescribe.length() - 2);
            }
        }
        // 临时性任务不需要"每"的前缀
        String msg = String.format(CycleType.T.getValue().equals(checkRate) ? NotifyContents.PRODUCE_TEMP_TASK : NotifyContents.PRODUCE_TASK
                , rateDescribe, planName, dateFormater.format(new Date()));
        if (generateCheck){
            notify.setNoticeUser(userId)
                    .setNoticeContent(msg)
                    .setBusinessType(AppBusinessNoticeTypes.INVESTIGATE.getType())
                    .setNoticeTitle(AppNotifyContentPre.TASK_NOTIFY)
                    .setSendTime(new Date())
                    .setRefId(task.getId());
            commonBusinessNoticeService.save(notify);
        }
        if (needPush && generateCheck) {
            noticeWaitingTriggerListService.save(new HashSet<>(Lists.newArrayList(userId)), AppBusinessNoticeTypes.INVESTIGATE.getType(),
                    msg, BusinessTypeEnum.INVESTIGATE_GEN_TASK.getBusinessCode(), task.getId().toString(), new Date());
        }
        indexNotifyService.generateInvestigateTaskNotify(Lists.newArrayList(userId), AppBusinessNoticeTypes.INVESTIGATE.getType(), msg, task);
    }

    /**
     * 任务即将逾期时给出通知
     *
     * @param task
     * @param executor
     * @param needPush
     */
    private void addTaskComingDeadNotify(BpInvestigateTask task, List<Long> executor, NotifyListsBean notifyLists, boolean needPush,boolean expireCheck) {
        String pointName = riskPointService.findById(inspectExceptionAlarmMesageMapper.getRiskPointByplanId(task.getPlanId())).getName();
        ArrayList<CommonBusinessNotice> commonBusinessNotices = new ArrayList<>();
        SimpleDateFormat dateFormater = null;
        Date sendDate = null;
        CycleType cycle = CycleType.find(task.getPeriodUnit());
        // 周期类型决定提前时间和时间格式
        switch (cycle) {
            case Y:
            case M:
                sendDate = DateUtil.offsetDay(task.getDeadline(), -2);
                dateFormater = SimpleDateFormatCache.getYmdhm();
                break;
            case W:
            case D:
                sendDate = DateUtil.offsetHour(task.getDeadline(), -2);
                dateFormater = SimpleDateFormatCache.getHm();
                break;
            case H:
                sendDate = DateUtil.offsetMinute(task.getDeadline(), -30);
                dateFormater = SimpleDateFormatCache.getHm();
                break;
        }
        String msg = String.format(NotifyContents.WILL_TIME_OUT, task.getCheckRateDescribe(), pointName, dateFormater.format(task.getDeadline()));
        if (expireCheck){
            for (Long userId : executor) {
                CommonBusinessNotice appNotify = new CommonBusinessNotice();
                appNotify.setBusinessType(AppBusinessNoticeTypes.INVESTIGATE.getType())
                        .setNoticeContent(msg)
                        .setNoticeUser(userId)
                        .setNoticeTitle(AppNotifyContentPre.TASK_WILL_TIME_OUT_NOTIFY)
                        .setSendTime(sendDate)
                        .setRefId(task.getId());
                appNotify.setOrgCode(task.getOrgCode());
                appNotify.setTenentId(task.getTenentId());
                commonBusinessNotices.add(appNotify);
            }
            // app通知中心
            notifyLists.getCommonBusinessNotifyList().addAll(commonBusinessNotices);
        }
        // 个推,可配置开关
        if (needPush && expireCheck) {
            noticeWaitingTriggerListService.saveInRam(new HashSet<>(executor),
                    AppBusinessNoticeTypes.INVESTIGATE.getType(), msg,
                    BusinessTypeEnum.INVESTIGATE_TASK_TIME_OUT.getBusinessCode(),
                    task.getId().toString(), sendDate, notifyLists.getGeTuiNotifyList());
        }
        // commonBusinessNoticeService.saveBatch(commonBusinessNotices);
    }

    private BpInvestigateTaskSummary saveTaskSummary(Long tenantId, String orgCode, Date curDate, Long userId) {
        BpInvestigateTaskSummary taskSummary;
        taskSummary = new BpInvestigateTaskSummary();
        taskSummary.setId(YitIdHelper.nextId());
        taskSummary.setCheckName(Constants.RISK_CONTROL_CHECK_NAME_DEFAULT);
        taskSummary.setCheckType(Constants.RISK_CONTROL_CHECK_TYPE_DEFAULT);
        taskSummary.setCheckDate(curDate);
        taskSummary.setCheckStatus(InvestigateTaskStatus.NO.getStatus());
        taskSummary.setOrgCode(orgCode);
        taskSummary.setTenentId(tenantId);
        taskSummary.setUserId(userId);
        // bpInvestigateTaskSummaryService.save(taskSummary);
        return taskSummary;
    }


    //是否需要生成任务: true 无需生成
    private boolean needProduceTask(BpInvestigatePlan plan, Date lastProduceTime) {
        LocalDate lastProduceDate = null;
        if (lastProduceTime != null) {
            lastProduceDate = Instant.ofEpochMilli(lastProduceTime.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
        }
        String checkRate = plan.getCheckRate();
        if ("临时性".equals(checkRate)) {
            LocalDate planDate = plan.getPlanDate();
            LocalDate planEndDate = plan.getPlanEndDate();
            LocalDate now = LocalDate.now();
            if (planEndDate == null) {
                if (!LocalDate.now().equals(planDate)) {
                    return true;
                }
            } else {
                if (now.isAfter(planEndDate) || now.isBefore(planDate)) {
                    return true;
                }
                try {
                    List<BpInvestigateTask> taskList = taskDao.findByPlanIdAndCheckDate(plan.getId(), DateTimeUtil.localDate2Date(planDate), DateTimeUtil.localDate2Date(planEndDate));
                    if (taskList.size() > 0) {
                        return true;
                    }
                } catch (Exception e) {
                    log.error("check period error", e);
                }
                if (plan.getLastProduceDate() != null) {
                    return true;
                }
            }
        } else {
            String periodUnit = plan.getPeriodUnit();
            Integer periodDays = plan.getPeriodDays();
            String periodJson = plan.getPeriodJson();
            //日
            if ("日".equals(periodUnit)) {
                //按天
                if (periodDays != null) {
                    LocalDate needDate = LocalDate.now();
                    if (lastProduceDate != null && !lastProduceDate.equals(needDate)) {
                        needDate = lastProduceDate.plusDays(periodDays);
                    }
                    if (LocalDate.now().isBefore(needDate)) {
                        return true;
                    }
                }
                //按周
                else if (periodJson != null) {
                    List<Integer> weeks = JSON.parseArray(periodJson, Integer.class);
                    int dayOfWeek = LocalDate.now().getDayOfWeek().getValue();
                    if (!weeks.contains(dayOfWeek)) {
                        return true;
                    }
                }
            }
            //每几月
            else if ("每几月".equals(periodUnit)) {
                if (plan.getMonthStartDate() == null || plan.getPeriodDays() == null) {
                    return true;
                }
                if (lastProduceDate == null) {
                    // 第一次生成任务, 当前时间在规则时间范围内则生成任务
                    if (checkInMonthPeriod(DateUtil.beginOfDay(new Date()), plan.getMonthStartDate(), plan.getPeriodDays())) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    // 上次所在月份， 跟当日所在月份进行相差在，1到2个周期内，则生成任务，否则不生成任务, -1代表没有找到该周期
                    int lastStartMonth = getPeriodStartMonth(DateTimeUtil.localDate2Date(lastProduceDate), plan.getMonthStartDate(), plan.getPeriodDays());
                    int thisStartMonth = getPeriodStartMonth(DateUtil.beginOfDay(new Date()), plan.getMonthStartDate(), plan.getPeriodDays());
                    if (thisStartMonth == -1) {
                        // 本次不在周期范围内
                        return true;
                    } else if (lastStartMonth == -1) {
                        // 上次不在周期范围，说明开始月份变大，则要生成任务
                        return false;
                    } else {
                        // 时间大于等于1个周期则生成任务
                        if (thisStartMonth - lastStartMonth >= plan.getPeriodDays()) {
                            return false;
                        } else {
                            return true;
                        }
                    }

                }
            }
            // 月
            else {
                LocalDate needDate = LocalDate.now();
                if (lastProduceDate != null && lastProduceDate.getMonth().getValue() != needDate.getMonth().getValue()) {
                    needDate = lastProduceDate.plusMonths(periodDays);
                }
                if (LocalDate.now().getMonth().getValue() != needDate.getMonth().getValue()) {
                    return true;
                }
                List<Integer> days = JSON.parseArray(periodJson, Integer.class);
                int dayOfMonth = LocalDate.now().getDayOfMonth();
                if (!days.contains(dayOfMonth)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static int getPeriodStartMonth(Date lastProduceDate, Integer startMonth, Integer period) {
        lastProduceDate = DateUtil.beginOfDay(lastProduceDate);
        int start = startMonth;

        if (period == null || period.intValue() == 0) {
            return -1;
        }
        while (start <= 12) {
            LocalDate now = LocalDate.now();
            LocalDate localDate = now.withMonth(start);
            LocalDate local = localDate.with(TemporalAdjusters.firstDayOfMonth());
            Date startDate = DateTimeUtil.localDate2Date(local);

            Date endDate = DateUtil.beginOfDay(DateUtil.offsetMonth(startDate, period));
            if (lastProduceDate.getTime() >= startDate.getTime() && lastProduceDate.getTime() < endDate.getTime()) {
                return start;
            }

            start += period;
        }
        return -1;
    }

    private static boolean checkInMonthPeriod(Date lastDate, Integer startMonth, Integer period) {
        lastDate = DateUtil.beginOfDay(lastDate);
        // 在时间范围内则生成
        if (startMonth != null) {
            int start = startMonth;

            if (period.intValue() == 0) {
                return false;
            }
            while (start <= 12) {
                LocalDate now = LocalDate.now();
                LocalDate localDate = now.withMonth(start);
                LocalDate local = localDate.with(TemporalAdjusters.firstDayOfMonth());
                Date startDate = DateTimeUtil.localDate2Date(local);

                Date endDate = DateUtil.beginOfDay(DateUtil.offsetMonth(startDate, period));
                if (lastDate.getTime() >= startDate.getTime() && lastDate.getTime() < endDate.getTime()) {
                    return true;
                }

                start += period;
            }
            return false;
        } else {
            return false;
        }
    }

    /**
     * 计划生成任务缓存key的前缀, 精确到人
     */
    private String getInvestigatePlanKey(BpInvestigatePlan plan) {
        Long tenentId = plan.getTenentId();
        String orgCode = plan.getOrgCode().substring(0, 7);
        return "investigate" + ":" + tenentId + ":" + orgCode + ":" + plan.getId() + ":" + LocalDate.now() + ":";
    }

    private List<BpBaseUserBean> getBpBaseUserBeans(String remindUserJson) {
        List<Long> remindUserIdList = JSONArray.parseArray(remindUserJson, Long.class);
        List<BpBaseUserBean> userBeanList = new ArrayList<>(remindUserIdList.size());
        if (!CollectionUtils.isEmpty(remindUserIdList)) {
            Map<Long, String> userMap = baseUserDao.findAllById(remindUserIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getRealName()));
            remindUserIdList.forEach(r -> userBeanList.add(new BpBaseUserBean(r, userMap.getOrDefault(r, ""))));
        }
        return userBeanList;
    }

    /**
     * app排查待办
     */
    @Override
    public List<BpInvestigateAppTaskDto> appNeedDo() {
        Long currentUserId = Context.getCurrentUserId();
        List<BpInvestigateTask> taskList = taskDao.findByUserIdAndCheckDateAndStatus(currentUserId, new Date(), InvestigateTaskStatus.NEED.getStatus());
        if (CollectionUtils.isEmpty(taskList)) {
            return Lists.newArrayList();
        }
        List<Long> planIdList = taskList.stream().map(r -> r.getPlanId()).distinct().collect(Collectors.toList());
        List<BpInvestigatePlan> planList = planRepository.findAllById(planIdList);
        Map<Long, BpInvestigatePlan> planMap = planList.stream().collect(Collectors.toMap(r -> r.getId(), r -> r, (r, v) -> r));
        Map<Long, String> typeMap = getTypeMap(planList);
        List<BpInvestigateAppTaskDto> taskDtoList = Lists.newArrayList();
        for (BpInvestigateTask task : taskList) {
            Long planId = task.getPlanId();
            BpInvestigateAppTaskDto dto = new BpInvestigateAppTaskDto();
            dto.setPlanId(planId);
            BpInvestigatePlan plan = planMap.get(planId);
            dto.setPlanName(plan != null ? plan.getCheckPlanName() : "");
            dto.setCheckDate(LocalDate.now());
            dto.setTaskId(task.getId());
            dto.setTypeName(plan != null ? typeMap.getOrDefault(plan.getTypeId(), "") : "");
            taskDtoList.add(dto);
        }
        return taskDtoList;
    }

    /**
     * 排查任务条件分页
     */
    @Override
    public ResultBean taskPage(BpInvestigateTaskQueryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        String checkUserName = bean.getCheckUserName();
        List<Long> userIdList = Lists.newArrayList();
        if (!StringUtils.isEmpty(checkUserName)) {
            List<Long> ids = baseUserDao.findByNameLike(checkUserName);
            // 查无此人
            if (CollectionUtil.isEmpty(ids)) {
                return ResultBean.pageData(Lists.newArrayList(), 0);
            }
            userIdList.addAll(ids);
        }
        Specification<BpInvestigateTask> specification = (Root<BpInvestigateTask> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("planId"), bean.getPlanId()));
            if (!StringUtils.isEmpty(bean.getStatus())) {
                predicates.add(cb.equal(root.get("status"), bean.getStatus()));
            }
            if (bean.getCheckDate() != null) {
                predicates.add(cb.equal(root.get("checkDate"), bean.getCheckDate()));
            }
            if (!CollectionUtils.isEmpty(userIdList)) {
                CriteriaBuilder.In<Object> in = cb.in(root.get("userId"));
                for (Long aLong : userIdList) {
                    in.value(aLong);
                }
                predicates.add(in);
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<BpInvestigateTask> page = taskDao.findAll(specification, pageable);
        List<Long> planIdList = page.stream().map(r -> r.getPlanId()).distinct().collect(Collectors.toList());
        List<Long> userIds = page.stream().map(r -> r.getUserId()).distinct().collect(Collectors.toList());
        Map<Long, String> planMap = planRepository.findAllById(planIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getCheckPlanName()));
        Map<Long, String> userMap = baseUserDao.findAllById(userIds).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getRealName()));
        List<BpInvestigateTaskDto> dtoList = new ArrayList<>(page.getContent().size());
        for (BpInvestigateTask task : page) {
            BpInvestigateTaskDto dto = new BpInvestigateTaskDto();
            BeanUtils.copyProperties(task, dto);
            dto.setPlanName(planMap.get(dto.getPlanId()));
            dto.setUserName(userMap.get(dto.getUserId()));
            dtoList.add(dto);
        }
        return ResultBean.pageData(dtoList, page.getTotalElements());
    }

    /**
     * 返回当前登录人员排查计划列表
     *
     * @return
     */
    @Override
    public List<BpInvestigatePlan> getList() {
        List<BpInvestigatePlan> list = planRepository.findExecutorLike(String.valueOf(Context.getCurrentUserId()));
        List<BpInvestigatePlan> pList = new ArrayList<>();
        for (BpInvestigatePlan p : list) {
            String emergencyMembers = p.getExecutor();
            if (!StringUtils.isEmpty(emergencyMembers)) {
                List<Long> longs = JSONArray.parseArray(emergencyMembers, Long.class);
                for (Long aLong : longs) {
                    if (Context.getCurrentUserId().equals(aLong)) {
                        pList.add(p);
                    }
                }
            }
        }
        Collections.reverse(pList);
        return pList;
    }

    @Override
    public ResultBean openClose(Long id, String state) {
        Assert.notNull(id, "id cannot be null");
        Assert.isTrue("启用".equals(state) || "停用".equals(state), "state is unavailable");
        BpInvestigatePlan plan = planRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("此数据不存在"));
        String initiateStatus = plan.getInitiateStatus();
        if (state.equals(initiateStatus)) {
            return ResultBean.defaultSuccessResult();
        }
        //计划启用停用后将计划的上次生成时间置为null,使其重新开始逻辑生成任务
        plan.setLastProduceDate(null);
        plan.setInitiateStatus(state);
        planRepository.save(plan);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public void batchSwitch(BpInvestigatePlanReqBean bean){
        if (!CollectionUtils.isEmpty(bean.getIds())) {
            planRepository.updateInitiateStatusByIds(bean.getIds(),bean.getState());
        }

    }

    @Override
    public Integer changeStatusBatch(Integer flag, List<Long> contentIds) {
        List<BpInvestigatePlan> plans = planRepository.findByHiddenDangerContentIds(contentIds);
        if (CollectionUtil.isEmpty(plans)) {
            return 0;
        }
        plans.forEach(plan -> {
            // 计划由启用更改为禁用时，需抹去上次生成时间
            if (flag == 0 && PlanStatus.ENABLE.getDesc().equals(plan.getInitiateStatus())) {
                plan.setLastProduceDate(null);
            }
            plan.setInitiateStatus(PlanStatus.find(flag).getDesc());
        });
        List<BpInvestigatePlan> list = planRepository.saveAll(plans);
        AuditLogRecord r = new AuditLogRecord();
        r.setActionDefId(Long.valueOf(633));
        r.setCode(String.valueOf(new Date().getTime()));
        r.setHappenTime(new Date());
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        r.setIp(HttpUtils.getClientIp(request));
        r.setMac(null);
        r.setModule("分级管控-任务批量启停");
//        r.setModule("风险分级管控");
        StringBuffer stringBuffer = new StringBuffer();
        if (flag == 0){
            r.setOperationName("批量启用");
            stringBuffer.append("启用"+list.size()+"条:");
        }
        if (flag == 1){
            r.setOperationName("批量停用");
            stringBuffer.append("停用"+list.size()+"条:");
        }
        for (BpInvestigatePlan bpInvestigatePlan : list) {
            Long riskPointId = bpInvestigatePlan.getRiskPointId();
            RiskPoint riskPoint = riskPointService.findById(riskPointId);
            String paichaName = "岗位所有人员";
            if (bpInvestigatePlan.getUnitId() == null){
                String executor = bpInvestigatePlan.getExecutor();
                String substring = executor.substring(1, executor.length() - 1);
                List<String> objectList = Arrays.asList(substring.split(","));
                for (String string : objectList){
                    BaseUser baseUser = baseUserDao.getOne(Long.valueOf(string));
                    paichaName = baseUser.getRealName();
                }
            }
            stringBuffer.append("风险单元名称："+riskPoint.getName()+"-岗位名称："+bpInvestigatePlan.getCheckPlanName()+"-排查人员："+paichaName+"-排查周期："+bpInvestigatePlan.getCheckRateDescribe()+";");
        }
        r.setDescription(stringBuffer.toString());
        // 判断执行状态
        r.setResult((byte) 0);
        r.setUserId(Context.getCurrentUserId());
        r.setUserName(Context.getCurrentUser().getRealName());
        r.setOpObjectType("java.lang.Long");
        r.setOpObjectValue(String.valueOf(list.size()));
        auditLogProcessor.process(r);
        return list.size();
    }

    @Override
    public List<BpInvestigatePlan> findByRiskPointEvaluationId(Long riskPointEvaluationId) {
        return planRepository.findByRiskPointEvaluationId(riskPointEvaluationId);
    }

}
